> ## Documentation Index
> Fetch the complete documentation index at: https://test-8ad8522e-feat-ai-sre.mintlify.site/llms.txt
> Use this file to discover all available pages before exploring further.

# Android SDK Advanced Configuration

> Configure advanced features of Android RUM SDK, including custom events, user tracking, sampling control, and data security

<Note>
  **About Dependencies and Package Names**

  Flashduty Android SDK is fully compatible with the Datadog open-source protocol. In `build.gradle`, use `cloud.flashcat` group for dependencies, but in Kotlin/Java code, import classes from the `com.datadog.android` package. You can seamlessly leverage Datadog ecosystem documentation, examples, and best practices while enjoying Flashduty platform services.
</Note>

Flashduty Android RUM SDK provides rich advanced configuration options to help you customize data collection and context information based on business needs.

<Info>
  **Supported Configuration Scenarios:**

  * Enrich user sessions - Add custom views, actions, resources, and error information
  * Protect sensitive data - Mask personally identifiable information and sensitive data
  * Associate user sessions - Link user sessions with internal user identifiers
  * Control data volume - Optimize data collection through sampling and event filtering
  * Enhance context - Add custom attributes to data
</Info>

## Enrich User Sessions

### Custom Views

When using `ActivityViewTrackingStrategy` or `FragmentViewTrackingStrategy`, the RUM SDK automatically tracks views. You can also manually send custom RUM views when a view becomes visible or interactive.

<Tabs>
  <Tab title="Kotlin">
    ```kotlin theme={null}
    import com.datadog.android.rum.GlobalRumMonitor

    fun onResume() {
        GlobalRumMonitor.get().startView(viewKey, viewName, attributes)
    }

    fun onPause() {
        GlobalRumMonitor.get().stopView(viewKey, attributes)
    }
    ```
  </Tab>

  <Tab title="Java">
    ```java theme={null}
    import com.datadog.android.rum.GlobalRumMonitor;

    public void onResume() {
        GlobalRumMonitor.get().startView(viewKey, viewName, attributes);
    }

    public void onPause() {
        GlobalRumMonitor.get().stopView(viewKey, attributes);
    }
    ```
  </Tab>
</Tabs>

<Note>
  **Parameter Description:**

  * `viewKey` (String) - Unique identifier for the view, same `viewKey` used for `startView()` and `stopView()`
  * `viewName` (String) - Name of the view
  * `attributes` (`Map<String, Any?>`) - Attributes attached to the view (optional)
</Note>

### Custom Actions

In addition to auto-tracked user interactions, you can track specific custom user actions (like clicks, swipes, likes).

<Tabs>
  <Tab title="Kotlin">
    ```kotlin theme={null}
    import com.datadog.android.rum.GlobalRumMonitor
    import com.datadog.android.rum.RumActionType

    fun onUserInteraction() {
        GlobalRumMonitor.get().addAction(
            RumActionType.TAP,
            name,
            attributes
        )
    }
    ```
  </Tab>

  <Tab title="Java">
    ```java theme={null}
    import com.datadog.android.rum.GlobalRumMonitor;
    import com.datadog.android.rum.RumActionType;

    public void onUserInteraction() {
        GlobalRumMonitor.get().addAction(
            RumActionType.TAP,
            name,
            attributes
        );
    }
    ```
  </Tab>
</Tabs>

<Accordion title="RumActionType Enum Values">
  | Type                   | Description   | Use Case                    |
  | ---------------------- | ------------- | --------------------------- |
  | `RumActionType.TAP`    | Tap action    | Button, icon taps           |
  | `RumActionType.SCROLL` | Scroll action | List, page scrolling        |
  | `RumActionType.SWIPE`  | Swipe action  | Swipe transitions, gestures |
  | `RumActionType.CLICK`  | Click action  | General clicks              |
  | `RumActionType.CUSTOM` | Custom action | Business-specific actions   |
</Accordion>

### Custom Resources

In addition to auto-tracked resources, you can manually track specific custom resources (like network requests, third-party library loading).

<Tabs>
  <Tab title="Kotlin">
    ```kotlin theme={null}
    import com.datadog.android.rum.GlobalRumMonitor
    import com.datadog.android.rum.RumResourceKind

    fun loadResource() {
        GlobalRumMonitor.get().startResource(resourceKey, method, url, attributes)
    }

    fun resourceLoadSuccess() {
        GlobalRumMonitor.get().stopResource(
            resourceKey, statusCode, size, 
            RumResourceKind.NATIVE, attributes
        )
    }

    fun resourceLoadError() {
        GlobalRumMonitor.get().stopResourceWithError(
            resourceKey, statusCode, message, source, throwable
        )
    }
    ```
  </Tab>

  <Tab title="Java">
    ```java theme={null}
    import com.datadog.android.rum.GlobalRumMonitor;
    import com.datadog.android.rum.RumResourceKind;

    public void loadResource() {
        GlobalRumMonitor.get().startResource(resourceKey, method, url, attributes);
    }

    public void resourceLoadSuccess() {
        GlobalRumMonitor.get().stopResource(
            resourceKey, statusCode, size, 
            RumResourceKind.NATIVE, attributes
        );
    }

    public void resourceLoadError() {
        GlobalRumMonitor.get().stopResourceWithError(
            resourceKey, statusCode, message, source, throwable
        );
    }
    ```
  </Tab>
</Tabs>

<Accordion title="RumResourceKind Resource Types">
  | Type       | Description         | Use Case                |
  | ---------- | ------------------- | ----------------------- |
  | `BEACON`   | Beacon request      | Analytics reporting     |
  | `FETCH`    | Fetch request       | Modern async requests   |
  | `XHR`      | XHR request         | Traditional Ajax        |
  | `DOCUMENT` | Document resource   | HTML documents          |
  | `IMAGE`    | Image resource      | Image loading           |
  | `JS`       | JavaScript resource | JS files                |
  | `FONT`     | Font resource       | Font files              |
  | `CSS`      | CSS resource        | Style files             |
  | `MEDIA`    | Media resource      | Audio/video files       |
  | `NATIVE`   | Native resource     | Native module loading   |
  | `OTHER`    | Other resource      | Uncategorized resources |
</Accordion>

### Custom Errors

To record specific errors, notify the RUM SDK when an exception occurs:

<Tabs>
  <Tab title="Kotlin">
    ```kotlin theme={null}
    import com.datadog.android.rum.GlobalRumMonitor

    GlobalRumMonitor.get().addError(
        message,
        source,
        throwable,
        attributes
    )
    ```
  </Tab>

  <Tab title="Java">
    ```java theme={null}
    import com.datadog.android.rum.GlobalRumMonitor;

    GlobalRumMonitor.get().addError(
        message,
        source,
        throwable,
        attributes
    );
    ```
  </Tab>
</Tabs>

<Note>
  For more error reporting details, see [Android Error Reporting](/en/rum/error-tracking/erro-reporting/android).
</Note>

### Custom Timing

In addition to RUM SDK's default performance metrics, you can use the `addTiming` API to measure the duration of key operations. Timing is an offset relative to the current RUM view start time.

<Tabs>
  <Tab title="Kotlin">
    ```kotlin theme={null}
    import com.datadog.android.rum.GlobalRumMonitor

    fun onHeroImageLoaded() {
        GlobalRumMonitor.get().addTiming("hero_image")
    }
    ```
  </Tab>

  <Tab title="Java">
    ```java theme={null}
    import com.datadog.android.rum.GlobalRumMonitor;

    public void onHeroImageLoaded() {
        GlobalRumMonitor.get().addTiming("hero_image");
    }
    ```
  </Tab>
</Tabs>

<Tip>
  After setting timing, access it via `@view.custom_timings.<timing_name>`, e.g., `@view.custom_timings.hero_image`.
</Tip>

### Set User Information

The RUM SDK supports standard user information.

<Tabs>
  <Tab title="Kotlin">
    ```kotlin theme={null}
    import com.datadog.android.rum.GlobalRumMonitor

    GlobalRumMonitor.get().setUserInfo(
        id = "1234",
        name = "John Doe",
        email = "john@doe.com"
    )
    ```
  </Tab>

  <Tab title="Java">
    ```java theme={null}
    import com.datadog.android.rum.GlobalRumMonitor;

    GlobalRumMonitor.get().setUserInfo(
        "1234",
        "John Doe",
        "john@doe.com",
        null
    );
    ```
  </Tab>
</Tabs>

<Warning>
  Only standard user fields are supported: `id`, `name`, `email`, and `anonymous_id`. Other user attributes are not supported. If needed, configure them under `context`.
</Warning>

## Event and Data Management

### Clear All Data

Use `clearAllData` to clear all unsent data currently stored in the SDK:

<Tabs>
  <Tab title="Kotlin">
    ```kotlin theme={null}
    import com.datadog.android.Datadog

    Datadog.clearAllData()
    ```
  </Tab>

  <Tab title="Java">
    ```java theme={null}
    import com.datadog.android.Datadog;

    Datadog.clearAllData();
    ```
  </Tab>
</Tabs>

### Stop Data Collection

Use `stopInstance` to stop collecting data and clear all local data:

<Tabs>
  <Tab title="Kotlin">
    ```kotlin theme={null}
    import com.datadog.android.Datadog

    Datadog.stopInstance()
    ```
  </Tab>

  <Tab title="Java">
    ```java theme={null}
    import com.datadog.android.Datadog;

    Datadog.stopInstance();
    ```
  </Tab>
</Tabs>

<Warning>
  After you call `stopInstance()`, the SDK stops working entirely. You have to initialize it again to resume data collection.
</Warning>

### Control Event Batch Upload

The RUM SDK uploads events in batches automatically. You can control that behavior with configuration parameters:

<Tabs>
  <Tab title="Kotlin">
    ```kotlin theme={null}
    import com.datadog.android.core.configuration.Configuration
    import com.datadog.android.core.configuration.UploadFrequency

    val configuration = Configuration.Builder(
        clientToken = clientToken,
        env = environmentName,
        variant = appVariantName
    )
        .setBatchSize(batchSize)
        .setUploadFrequency(UploadFrequency.FREQUENT)
        .build()
    ```
  </Tab>

  <Tab title="Java">
    ```java theme={null}
    import com.datadog.android.core.configuration.Configuration;
    import com.datadog.android.core.configuration.UploadFrequency;

    Configuration configuration = new Configuration.Builder(
        clientToken, environmentName, appVariantName
    )
        .setBatchSize(batchSize)
        .setUploadFrequency(UploadFrequency.FREQUENT)
        .build();
    ```
  </Tab>
</Tabs>

<Accordion title="UploadFrequency">
  | Frequency  | Description               | When to use                                 |
  | ---------- | ------------------------- | ------------------------------------------- |
  | `FREQUENT` | Upload often              | Scenarios that need data to arrive quickly  |
  | `AVERAGE`  | Upload at a moderate pace | Default. Balances performance and freshness |
  | `RARE`     | Upload sparingly          | Saves bandwidth and battery                 |
</Accordion>

### Set Remote Log Threshold

You can define a minimum log level for remotely recorded messages. Logs below that level are not sent to Flashduty:

<Tabs>
  <Tab title="Kotlin">
    ```kotlin theme={null}
    import com.datadog.android.log.Logs
    import com.datadog.android.log.LogsConfiguration
    import android.util.Log

    val logsConfig = LogsConfiguration.Builder()
        .setRemoteSampleRate(100f)
        .setRemoteLogThreshold(Log.WARN)
        .build()
        
    Logs.enable(logsConfig)
    ```
  </Tab>

  <Tab title="Java">
    ```java theme={null}
    import com.datadog.android.log.Logs;
    import com.datadog.android.log.LogsConfiguration;
    import android.util.Log;

    LogsConfiguration logsConfig = new LogsConfiguration.Builder()
        .setRemoteSampleRate(100f)
        .setRemoteLogThreshold(Log.WARN)
        .build();
        
    Logs.enable(logsConfig);
    ```
  </Tab>
</Tabs>

<Note>
  With a `Log.WARN` threshold, only WARN and ERROR logs are uploaded. DEBUG and INFO logs are filtered out.
</Note>

## Track Custom Global Attributes

Beyond the default attributes the RUM SDK captures automatically, you can attach extra context to RUM events, such as custom attributes.

<Check>
  **What custom attributes are for:**

  * Filter and group user behavior by business information, such as cart state, user tier, or marketing campaign
  * Follow the browsing path of a specific user
  * Understand which users are hit hardest by an error
  * Monitor performance for your most important users
</Check>

### Track User Sessions

To identify user sessions, call the `setUserInfo` API after initializing the SDK:

<Tabs>
  <Tab title="Kotlin">
    ```kotlin theme={null}
    import com.datadog.android.rum.GlobalRumMonitor

    GlobalRumMonitor.get().setUserInfo(
        id = "1234",
        name = "John Doe",
        email = "john@doe.com"
    )
    ```
  </Tab>

  <Tab title="Java">
    ```java theme={null}
    import com.datadog.android.rum.GlobalRumMonitor;

    GlobalRumMonitor.get().setUserInfo("1234", "John Doe", "john@doe.com", null);
    ```
  </Tab>
</Tabs>

<Warning>
  Only the standard user fields are supported: `id`, `name`, `email` and `anonymous_id`. Other user properties are not. If you need more, put them in the `context` field.
</Warning>

<Note>
  **Parameters:**

  * `id` (String) - Unique user identifier
  * `name` (String) - Friendly user name, shown in the RUM UI by default
  * `email` (String) - User email, shown when no name is available
  * All of these are optional, but you should provide at least one
</Note>

### Track Attributes

Global attributes are attached to every RUM event and are useful for adding shared context.

**Add a global attribute:**

<Tabs>
  <Tab title="Kotlin">
    ```kotlin theme={null}
    import com.datadog.android.rum.GlobalRumMonitor

    GlobalRumMonitor.get().addAttribute("key", "value")
    ```
  </Tab>

  <Tab title="Java">
    ```java theme={null}
    import com.datadog.android.rum.GlobalRumMonitor;

    GlobalRumMonitor.get().addAttribute("key", "value");
    ```
  </Tab>
</Tabs>

**Remove a global attribute:**

<Tabs>
  <Tab title="Kotlin">
    ```kotlin theme={null}
    import com.datadog.android.rum.GlobalRumMonitor

    GlobalRumMonitor.get().removeAttribute("key")
    ```
  </Tab>

  <Tab title="Java">
    ```java theme={null}
    import com.datadog.android.rum.GlobalRumMonitor;

    GlobalRumMonitor.get().removeAttribute("key");
    ```
  </Tab>
</Tabs>

## Track Widgets

<Note>
  Widgets are not tracked automatically. To monitor widget interactions, call the API yourself.
</Note>

<Tabs>
  <Tab title="Kotlin">
    ```kotlin theme={null}
    import com.datadog.android.rum.GlobalRumMonitor
    import com.datadog.android.rum.RumActionType

    fun onWidgetClicked() {
        GlobalRumMonitor.get().addAction(
            RumActionType.TAP,
            "widget_clicked",
            mapOf("widget_name" to "HomeWidget")
        )
    }
    ```
  </Tab>

  <Tab title="Java">
    ```java theme={null}
    import com.datadog.android.rum.GlobalRumMonitor;
    import com.datadog.android.rum.RumActionType;
    import java.util.HashMap;
    import java.util.Map;

    public void onWidgetClicked() {
        Map<String, Object> attributes = new HashMap<>();
        attributes.put("widget_name", "HomeWidget");
        GlobalRumMonitor.get().addAction(
            RumActionType.TAP,
            "widget_clicked",
            attributes
        );
    }
    ```
  </Tab>
</Tabs>

## Initialization Parameters

When you initialize the Flashduty Android SDK, `Configuration.Builder` gives you several options.

### Automatically Track Views

To track views (activities and fragments) automatically, call `useViewTrackingStrategy` during initialization:

<Tabs>
  <Tab title="Kotlin">
    ```kotlin theme={null}
    import com.datadog.android.rum.RumConfiguration
    import com.datadog.android.rum.tracking.ActivityViewTrackingStrategy

    val rumConfig = RumConfiguration.Builder(applicationId)
        .useViewTrackingStrategy(ActivityViewTrackingStrategy(true))
        .build()
    ```
  </Tab>

  <Tab title="Java">
    ```java theme={null}
    import com.datadog.android.rum.RumConfiguration;
    import com.datadog.android.rum.tracking.ActivityViewTrackingStrategy;

    RumConfiguration rumConfig = new RumConfiguration.Builder(applicationId)
        .useViewTrackingStrategy(new ActivityViewTrackingStrategy(true))
        .build();
    ```
  </Tab>
</Tabs>

<Accordion title="Available tracking strategies">
  | Strategy                         | Parameters                      | Description                              | When to use                             |
  | -------------------------------- | ------------------------------- | ---------------------------------------- | --------------------------------------- |
  | `ActivityViewTrackingStrategy`   | `trackExtras`                   | Tracks each activity as its own view     | Traditional activity-based architecture |
  | `FragmentViewTrackingStrategy`   | `trackArguments`                | Tracks each fragment as its own view     | Fragment-oriented apps                  |
  | `MixedViewTrackingStrategy`      | `trackExtras`, `trackArguments` | Tracks both activities and fragments     | Mixed architecture                      |
  | `NavigationViewTrackingStrategy` | `navigationViewId`              | Tracks Navigation component destinations | Apps using Jetpack Navigation           |
</Accordion>

### Automatically Track Network Requests

To track HTTP requests automatically, see the OkHttp interceptor setup in the [SDK integration guide](./sdk-integration#enable-distributed-trace-tracking).

### Automatically Track Apollo GraphQL Requests

If you use the Apollo GraphQL client for network calls, you can enable automatic tracking.

<Steps>
  <Step title="Add the Apollo dependency">
    Add the dependency to your app's `build.gradle` file:

    ```groovy build.gradle theme={null}
    dependencies {
        implementation "cloud.flashcat:dd-sdk-android-apollo:<latest-version>"
    }
    ```

    <Tip>
      Check the [Maven Central versions page](https://central.sonatype.com/artifact/cloud.flashcat/dd-sdk-android-core/versions) for the latest version number.
    </Tip>
  </Step>

  <Step title="Configure the Apollo client">
    <Tabs>
      <Tab title="Kotlin">
        ```kotlin theme={null}
        import com.apollographql.apollo.ApolloClient
        import com.apollographql.apollo.network.okHttpClient
        import com.datadog.android.apollo.DatadogApolloInterceptor

        val apolloClient = ApolloClient.Builder()
            .serverUrl("GraphQL endpoint")
            .addInterceptor(DatadogApolloInterceptor())
            .okHttpClient(okHttpClient)
            .build()
        ```
      </Tab>

      <Tab title="Java">
        ```java theme={null}
        import com.apollographql.apollo.ApolloClient;
        import com.datadog.android.apollo.DatadogApolloInterceptor;

        ApolloClient apolloClient = new ApolloClient.Builder()
            .serverUrl("GraphQL endpoint")
            .addInterceptor(new DatadogApolloInterceptor())
            .okHttpClient(okHttpClient)
            .build();
        ```
      </Tab>
    </Tabs>

    <Check>
      Flashduty tracing headers are added to your GraphQL requests automatically, which makes them traceable.
    </Check>
  </Step>
</Steps>

<Warning>
  **Limitations:**

  * Only Apollo version **4** is supported
  * Only `query` and `mutation` operations are tracked; `subscription` operations are not
</Warning>

**Send GraphQL payloads (optional):**

<Tabs>
  <Tab title="Kotlin">
    ```kotlin theme={null}
    DatadogApolloInterceptor(sendGraphQLPayloads = true)
    ```
  </Tab>

  <Tab title="Java">
    ```java theme={null}
    new DatadogApolloInterceptor(true)
    ```
  </Tab>
</Tabs>

### Automatically Track Long Tasks

Long-running work on the main thread can hurt your app's visual performance and responsiveness. The SDK can detect and track long tasks automatically.

<Tabs>
  <Tab title="Kotlin">
    ```kotlin theme={null}
    import com.datadog.android.rum.RumConfiguration

    // Use the default threshold (100ms)
    val rumConfig = RumConfiguration.Builder(applicationId)
        .trackLongTasks(durationThreshold)
        .build()

    // Use a custom threshold (250ms)
    val rumConfig = RumConfiguration.Builder(applicationId)
        .trackLongTasks(250L)
        .build()
    ```
  </Tab>

  <Tab title="Java">
    ```java theme={null}
    import com.datadog.android.rum.RumConfiguration;

    // Use the default threshold (100ms)
    RumConfiguration rumConfig = new RumConfiguration.Builder(applicationId)
        .trackLongTasks(durationThreshold)
        .build();

    // Use a custom threshold (250ms)
    RumConfiguration rumConfig = new RumConfiguration.Builder(applicationId)
        .trackLongTasks(250L)
        .build();
    ```
  </Tab>
</Tabs>

<Note>
  The default threshold is **100ms**. Adjust it to match your app's performance requirements.
</Note>

## Modify or Discard RUM Events

To modify some attributes of a RUM event before it is batched, or to discard events entirely, provide an implementation of `EventMapper<T>` at initialization.

<Tabs>
  <Tab title="Kotlin">
    ```kotlin theme={null}
    import com.datadog.android.rum.RumConfiguration

    val rumConfig = RumConfiguration.Builder(applicationId)
        .setErrorEventMapper(rumErrorEventMapper)
        .setActionEventMapper(rumActionEventMapper)
        .setResourceEventMapper(rumResourceEventMapper)
        .setViewEventMapper(rumViewEventMapper)
        .setLongTaskEventMapper(rumLongTaskEventMapper)
        .build()
    ```
  </Tab>

  <Tab title="Java">
    ```java theme={null}
    import com.datadog.android.rum.RumConfiguration;

    RumConfiguration rumConfig = new RumConfiguration.Builder(applicationId)
        .setErrorEventMapper(rumErrorEventMapper)
        .setActionEventMapper(rumActionEventMapper)
        .setResourceEventMapper(rumResourceEventMapper)
        .setViewEventMapper(rumViewEventMapper)
        .setLongTaskEventMapper(rumLongTaskEventMapper)
        .build();
    ```
  </Tab>
</Tabs>

### Modifiable Event Attributes

When you implement the `EventMapper<T>` interface, only some attributes can be modified for each event type:

<AccordionGroup>
  <Accordion title="ViewEvent modifiable attributes">
    | Attribute key   | Description                                     |
    | --------------- | ----------------------------------------------- |
    | `view.referrer` | URL that linked to the initial view of the page |
    | `view.url`      | URL of the view                                 |
    | `view.name`     | Name of the view                                |
  </Accordion>

  <Accordion title="ActionEvent modifiable attributes">
    | Attribute key        | Description                                     |
    | -------------------- | ----------------------------------------------- |
    | `action.target.name` | Target name                                     |
    | `view.referrer`      | URL that linked to the initial view of the page |
    | `view.url`           | URL of the view                                 |
    | `view.name`          | Name of the view                                |
  </Accordion>

  <Accordion title="ErrorEvent modifiable attributes">
    | Attribute key        | Description                                     |
    | -------------------- | ----------------------------------------------- |
    | `error.message`      | Error message                                   |
    | `error.stack`        | Stack trace of the error                        |
    | `error.resource.url` | URL of the resource                             |
    | `view.referrer`      | URL that linked to the initial view of the page |
    | `view.url`           | URL of the view                                 |
    | `view.name`          | Name of the view                                |
  </Accordion>

  <Accordion title="ResourceEvent modifiable attributes">
    | Attribute key   | Description                                     |
    | --------------- | ----------------------------------------------- |
    | `resource.url`  | URL of the resource                             |
    | `view.referrer` | URL that linked to the initial view of the page |
    | `view.url`      | URL of the view                                 |
    | `view.name`     | Name of the view                                |
  </Accordion>

  <Accordion title="LongTaskEvent modifiable attributes">
    | Attribute key   | Description                                     |
    | --------------- | ----------------------------------------------- |
    | `view.referrer` | URL that linked to the initial view of the page |
    | `view.url`      | URL of the view                                 |
    | `view.name`     | Name of the view                                |
  </Accordion>
</AccordionGroup>

<Warning>
  If your `EventMapper<T>` implementation returns `null`, the event is discarded and never sent to Flashduty.
</Warning>

### Example: Discard Sensitive Errors

<Tabs>
  <Tab title="Kotlin">
    ```kotlin theme={null}
    val rumConfig = RumConfiguration.Builder(applicationId)
        .setErrorEventMapper { errorEvent ->
            if (errorEvent.error.message?.contains("sensitive_data") == true) {
                null // Discard errors that contain sensitive data
            } else {
                errorEvent
            }
        }
        .build()
    ```
  </Tab>

  <Tab title="Java">
    ```java theme={null}
    RumConfiguration rumConfig = new RumConfiguration.Builder(applicationId)
        .setErrorEventMapper(errorEvent -> {
            if (errorEvent.error.message != null &&
                errorEvent.error.message.contains("sensitive_data")) {
                return null; // Discard errors that contain sensitive data
            } else {
                return errorEvent;
            }
        })
        .build();
    ```
  </Tab>
</Tabs>

## Get the RUM Session ID

Retrieving the RUM session ID is useful for troubleshooting. You can attach it to support requests, emails or error reports so the support team can find the user's session in Flashduty.

<Tabs>
  <Tab title="Kotlin">
    ```kotlin theme={null}
    import com.datadog.android.rum.GlobalRumMonitor

    GlobalRumMonitor.get().getCurrentSessionId { sessionId ->
        currentSessionId = sessionId
    }
    ```
  </Tab>

  <Tab title="Java">
    ```java theme={null}
    import com.datadog.android.rum.GlobalRumMonitor;

    GlobalRumMonitor.get().getCurrentSessionId(sessionId -> {
        currentSessionId = sessionId;
    });
    ```
  </Tab>
</Tabs>

<Tip>
  You can read the RUM session ID at runtime without waiting for the `sessionStarted` event.
</Tip>

## Sampling Control

By default, RUM collects data for every session. You can reduce the number of collected sessions by setting a sample rate through the `sessionSampleRate` parameter.

<Tabs>
  <Tab title="Kotlin">
    ```kotlin theme={null}
    import com.datadog.android.rum.RumConfiguration

    val rumConfig = RumConfiguration.Builder(applicationId)
        .setSessionSampleRate(90.0f) // Collect 90% of sessions
        .build()
    ```
  </Tab>

  <Tab title="Java">
    ```java theme={null}
    import com.datadog.android.rum.RumConfiguration;

    RumConfiguration rumConfig = new RumConfiguration.Builder(applicationId)
        .setSessionSampleRate(90.0f) // Collect 90% of sessions
        .build();
    ```
  </Tab>
</Tabs>

<Note>
  Sample rate range: **0.0 - 100.0**

  * `100.0` - Collect every session (default)
  * `50.0` - Collect 50% of sessions
  * `0.0` - Collect no sessions
</Note>

<Warning>
  Sampled-out sessions collect no page views and none of the related telemetry data.
</Warning>

### Remote configuration: adjust the sample rate from the console

Since **0.7.0**, the session sample rate can be changed on the Remote configuration page of the Flashcat console without shipping a new release of your app. The feature is off by default; turn it on at initialization:

<Tabs>
  <Tab title="Kotlin">
    ```kotlin theme={null}
    val rumConfig = RumConfiguration.Builder(applicationId)
        .setSessionSampleRate(90.0f)          // Used until the console's settings arrive
        .setRemoteConfigurationEnabled(true)  // Let the console set the session sample rate
        .build()
    ```
  </Tab>

  <Tab title="Java">
    ```java theme={null}
    RumConfiguration rumConfig = new RumConfiguration.Builder(applicationId)
        .setSessionSampleRate(90.0f)          // Used until the console's settings arrive
        .setRemoteConfigurationEnabled(true)  // Let the console set the session sample rate
        .build();
    ```
  </Tab>
</Tabs>

How a change takes effect:

* The SDK asks the console for the configuration once at startup and once whenever a new session starts. What it receives is used to draw the **next** session; a session already under way is never redrawn.
* The exception is a rate that **crosses zero** (from 0 to non-zero, or from non-zero to 0): the running session ends immediately and the next one is drawn under the new rate, so an emergency stop or a restart does not wait for sessions to rotate.
* When the request fails, times out or returns an unreadable response, the SDK keeps the values already in use; before the first configuration ever arrives, the value passed to `setSessionSampleRate` applies. The configuration is cached on the device, so the first session of the next cold start already uses it.
* The request carries only the client token, the environment, the app version and the SDK version, never user data, so it is not gated by the [tracking consent](#user-tracking-consent) state.
* In a private deployment the configuration endpoint sits beside the RUM intake under `/config` (for an intake of `https://host/api/v2/rum`, it is `https://host/api/v2/rum/config`). When you use `useCustomEndpoint`, make sure your gateway lets that path through.

#### Override the draw from your app

To guarantee that certain users are always collected (internal testers, a user whose issue you are investigating), `setBeforeSampling` lets the app have the last word before each draw. Return `null` to keep the incoming rate. A value outside 0..100, or a callback that throws, also leaves the incoming rate in place and never disrupts collection.

<Tabs>
  <Tab title="Kotlin">
    ```kotlin theme={null}
    val rumConfig = RumConfiguration.Builder(applicationId)
        .setRemoteConfigurationEnabled(true)
        .setBeforeSampling { context ->
            // context.sessionSampleRate is the rate from the console (or from init)
            // context.custom holds the key-values published under "Custom configuration"
            if (isInternalTester()) 100f else null
        }
        .build()
    ```
  </Tab>

  <Tab title="Java">
    ```java theme={null}
    RumConfiguration rumConfig = new RumConfiguration.Builder(applicationId)
        .setRemoteConfigurationEnabled(true)
        .setBeforeSampling(context -> isInternalTester() ? 100f : null)
        .build();
    ```
  </Tab>
</Tabs>

While the app is running you can also call `setForcedSession()` at any time to collect every session of this user from now until the process ends:

```kotlin theme={null}
GlobalRumMonitor.get().setForcedSession()
```

#### Read the console's custom configuration

The values published under "Custom configuration" on the Remote configuration page are delivered to the SDK verbatim and can be read with `getRemoteConfig()`. The SDK never interprets them; what they mean is entirely up to your app. It returns `null` when nothing is published or remote configuration is off.

```kotlin theme={null}
val custom = GlobalRumMonitor.get().getRemoteConfig()
val debugUsers = custom?.get("debugUsers") as? List<*>
```

<Warning>
  Custom configuration is visible to every client running the SDK. Never put secrets, tokens or personal data in it.
</Warning>

## User Tracking Consent

To comply with privacy regulations such as GDPR and CCPA, RUM lets you set the user tracking consent state at initialization.

### Consent States

| State                         | Behavior                                       | When to use                            |
| ----------------------------- | ---------------------------------------------- | -------------------------------------- |
| `TrackingConsent.GRANTED`     | Start collecting data and send it to Flashduty | The user has agreed to data collection |
| `TrackingConsent.NOT_GRANTED` | Collect no data                                | The user has declined data collection  |
| `TrackingConsent.PENDING`     | Collect data but do not send it                | Waiting for the user to decide         |

<Note>
  If you initialize with `TrackingConsent.PENDING`, the SDK starts collecting data but sends nothing until the consent state changes to `GRANTED`.
</Note>

### Change Consent State

You can change the consent state after initialization through the `setTrackingConsent` API:

<Tabs>
  <Tab title="Kotlin">
    ```kotlin theme={null}
    import com.datadog.android.Datadog
    import com.datadog.android.privacy.TrackingConsent

    Datadog.setTrackingConsent(TrackingConsent.GRANTED)
    ```
  </Tab>

  <Tab title="Java">
    ```java theme={null}
    import com.datadog.android.Datadog;
    import com.datadog.android.privacy.TrackingConsent;

    Datadog.setTrackingConsent(TrackingConsent.GRANTED);
    ```
  </Tab>
</Tabs>

## Best Practices

<AccordionGroup>
  <Accordion title="View tracking">
    * Call `startView` and `stopView` in the right lifecycle methods so views are not tracked twice
    * Use a unique `viewKey` for each view
  </Accordion>

  <Accordion title="Resource tracking">
    * When tracking resources manually, pair every `startResource` with a matching `stopResource` or `stopResourceWithError`
    * Avoid tracking internal resources or requests that fire very frequently
  </Accordion>

  <Accordion title="Event modification">
    * Only the attributes listed in the tables above can be modified; changes to any other attribute are ignored
    * Return `null` to discard the whole event
  </Accordion>

  <Accordion title="Performance">
    * Tune the sample rate and batch upload frequency together to balance data volume against overhead
    * Avoid slow work inside event callbacks
  </Accordion>
</AccordionGroup>

## Related Documentation

<CardGroup cols={3}>
  <Card title="SDK Integration" icon="plug" href="/en/rum/sdk/android/sdk-integration">
    Learn how to quickly integrate the RUM SDK
  </Card>

  <Card title="Data Collection" icon="database" href="/en/rum/sdk/android/data-collection">
    Learn about data types and attributes collected by the SDK
  </Card>

  <Card title="Compatibility" icon="check" href="/en/rum/sdk/android/compatible">
    Learn about SDK compatibility requirements
  </Card>
</CardGroup>
