
This is the official documentation for the Amplitude Analytics Android SDK.

{% callout type="deprecated" heading="" %}
This maintenance SDK only receives bug fixes until deprecation. Upgrade to the [Android Kotlin SDK](/docs/sdks/analytics/android/android-kotlin-sdk), which supports plugins, SDK integrations, and more.
{% /callout %}

## Install the SDK

{% callout type="tip" heading="" %}
Amplitude recommends Android Studio as an IDE and Gradle to manage dependencies. Use version 2.x, version 3.35.1 is invalid.
{% /callout %}

1. In the `build.gradle` file, add these dependencies. The SDK requires OkHTTP.

   ```java
   dependencies {
   implementation 'com.amplitude:android-sdk:2.+'
   implementation 'com.squareup.okhttp3:okhttp:4.2.2'
   }

   ```

2. Sync project with Gradle files.
3. To report events to Amplitude, add the INTERNET permission to your `AndroidManifest.xml` file:
   `<uses-permission android:name="android.permission.INTERNET" />`.
4. For Android 6.0 (Marshmallow) and higher, explicitly add permission to fetch the device [advertising ID](#advertiser-id).

After you install the SDK and its dependencies, import Amplitude into any file that uses it.

```java
import com.amplitude.api.Amplitude;
import com.amplitude.api.AmplitudeClient;
/*
Import any more files that are needed, use the SDK reference
http://amplitude.github.io/Amplitude-Android/
*/

```

## Core functions

The following functions make up the core of the Amplitude Analytics Android SDK.

### Initialize the SDK

You must initialize the SDK before you can instrument. The SDK requires the API key for your Amplitude project. Amplitude recommends adding this in `onCreate(...)` of your Activity class.

You can use the Android SDK anywhere after it's initialized in an Android application.

Accurate session tracking requires that you enable `enableForegroundTracking(getApplication())`. Amplitude disables this by default.

{% code-group %}
```java Java
AmplitudeClient client = Amplitude.getInstance()
 .initialize(getApplicationContext(), AMPLITUDE_API_KEY)
 .enableForegroundTracking(getApplication());

```

```kotlin Kotlin
val client = Amplitude.getInstance()
 .initialize(getApplicationContext(), AMPLITUDE_API_KEY)
 .enableForegroundTracking(application)

```
{% /code-group %}

`Amplitude.getInstance(String name)` can take a name that holds settings. This instance is now linked to the name and you can retrieve it somewhere else.

{% code-group %}
```java Java
AmplitudeClient client1 = Amplitude.getInstance("Andy_Client");
AmplitudeClient client2 = Amplitude.getInstance("Bob_Client");

//In the same file, or a different activity in the app
AmplitudeClient sameClient = Amplitude.getInstance("Andy_Client");

```

```kotlin Kotlin
val client1 = Amplitude.getInstance("Andy_Client")
val client2 = Amplitude.getInstance("Bob_Client")

//In the same file, or a different activity in the app
val sameClient = Amplitude.getInstance("Andy_Client")

```
{% /code-group %}

### Configuration options

| Name                           | Description                                                                                                                        | Default Value                 |
| ------------------------------ | ---------------------------------------------------------------------------------------------------------------------------------- | ----------------------------- |
| `eventUploadPeriodMillis`      | The amount of time the SDK waits before it attempts to upload unsent events to the server or reach the `eventUploadThreshold`.     | `30000`                       |
| `eventUploadThreshold`         | The SDK attempts to upload after the unsent event count exceeds this threshold or reaches the `eventUploadPeriodMillis` interval.  | `30`                          |
| `eventUploadMaxBatchSize`      | The maximum number of events sent with each upload request.                                                                        | `50`                          |
| `eventMaxCount`                | The maximum number of unsent events to keep on the device.                                                                         | `1000`                        |
| `identifyBatchIntervalMillis`  | The amount of time the SDK batches intercepted identify events.                                                                    | `30000`                       |
| `flushEventsOnClose`           | Flush unsent events on app close.                                                                                                  | `true`                        |
| `optOut`                       | Opt the user out of tracking.                                                                                                      | `false`                       |
| `trackingSessionEvents`        | Automatic tracking of "Start Session" and "End Session" events that count toward event volume.                                     | `false`                       |
| `sessionTimeoutMillis`         | The amount of time for session timeout when foreground tracking is disabled. Amplitude disables foreground tracking by default.    | `1800000`                     |
| `minTimeBetweenSessionsMillis` | The amount of time for session timeout when you enable foreground tracking through `enableForegroundTracking()`.                   | `300000`                      |
| `serverUrl`                    | The server URL that events upload to.                                                                                              | `https://api2.amplitude.com/` |
| `useDynamicConfig`             | Find the best server URL automatically based on user geo location.                                                                 | `false`                       |

### EU data residency

Starting with version 2.34.0, you can configure the server zone after you initialize the client to send data to Amplitude's EU servers. The SDK sends data based on the server zone when it's set.

The server zone configuration also supports dynamic configuration.

For earlier versions, configure the `serverURL` property after you initialize the client.

```java
// For versions starting from 2.34.0
// No need to call setServerUrl for sending data to Amplitude's EU servers
client.setServerZone(AmplitudeServerZone.EU);

// For earlier versions
client.setServerUrl("https://api.eu.amplitude.com");
```

### Send basic events

Events represent how users interact with your application. For example, "button click" may be an action you want to track.

```java
client.logEvent("Button Clicked");
```

### Send events with properties

Events can contain properties, which give more context about the event. For example, "hover time" may be a relevant event property for "button click."

{% code-group %}
```java Java
JSONObject eventProperties = new JSONObject();
try {
 eventProperties.put("Hover Time", 10).put("prop_2", "value_2");
} catch (JSONException e) {
 System.err.println("Invalid JSON");
 e.printStackTrace();
}
client.logEvent("Button Clicked", eventProperties);
// Note: You will also need to add two JSONObject imports to the code.
// import org.json.JSONException;
// import org.json.JSONObject;

```

```kotlin Kotlin
val eventProperties = JSONObject()
try {
  eventProperties.put("Hover Time", 10).put("prop_2", "value_2")
} catch (e: JSONException) {
  System.err.println("Invalid JSON")
  e.printStackTrace()
}
client.logEvent("Button Clicked", eventProperties)

```
{% /code-group %}

### Flush events

The SDK stores unsent events in a buffer and flushes (sends) them on app close by default. The SDK flushes events based on which criterion meets first: `eventUploadPeriodMillis` or `eventUploadThreshold`.

You can disable flushing or configure the upload period and the event upload threshold.

Disable flushing, change upload period, or change default event buffer:

```java
client.setFlushEventsOnClose(false); //Don't flush events

```

The default upload period is 30 seconds. Input is in milliseconds.

```java
Amplitude.getInstance(instanceName).setEventUploadPeriodMillis(100000); // Changes event upload period to 100 seconds

```

The default event buffer is 30. Input is an int.

```java
Amplitude.getInstance(instanceName).setEventUploadThreshold(4); // Changes event upload buffer to 4

```

To force the SDK to upload unsent events, use the method `uploadEvents`.

### Set user properties

{% callout type="note" heading="Privacy and tracking" %}
Don't track any user data that's against your privacy terms.
{% /callout %}

Use Identify to set the user properties of a particular user without sending an event.

The SDK supports these operations on individual user properties: `set`, `setOnce`, `unset`, `add`, `append`, `prepend`, `preInsert`, `postInsert`, and `remove`. Declare the operations through the Identify interface. You can chain together multiple operations in a single Identify object.

Pass the Identify object to the Amplitude client to send to the server. Starting from release v2.29.0, the SDK batches identify events with set operations and sends them as fewer events. This change doesn't affect how the set operations run. The `identifyBatchIntervalMillis` config manages the interval to flush the batched identify intercepts.

{% callout type="note" heading="" %}
If you send the Identify call after the event, the results of operations appear immediately in the user's profile area in the dashboard, but the results don't appear in chart results until another event is sent after the Identify call. The identify call only affects events going forward.
{% /callout %}

You can handle the identity of a user through the identify methods. Proper use of these methods connects events to the correct user as they move across devices, browsers, and other platforms.

Send an identify call that contains those user property operations to the Amplitude server to tie a user's events with specific user properties.

{% code-group %}
```java Java
Identify identify = new Identify();
identify.set("color", "green");
client.identify(identify);

```

```kotlin Kotlin
val identify = Identify()
identify["color"] = "green"
client.identify(identify)

```
{% /code-group %}

#### Set

`set` sets the value of a user property. You can also chain together multiple identify calls.

{% code-group %}
```java Java
Identify identify = new Identify().set("color", "green");

```

```kotlin Kotlin
val identify = Identify().set("color", "green")

```
{% /code-group %}

#### Set once

`setOnce` sets the value of a user property one time. Later calls using `setOnce` are ignored.

{% code-group %}
```java Java
Identify identify = new Identify().setOnce("color", "green");

```

```kotlin Kotlin
val identify = Identify().setOnce("color", "green")

```
{% /code-group %}

#### Add

`add` increments a user property by some numerical value. If the user property doesn't have a value set yet, the SDK initializes the property to 0 before it increments.

{% code-group %}
```java Java
Identify identify = new Identify().set("number_of_clients", 10);
//...
identify.add("number_of_clients", 5); //15
identify.add("annual_revenue", 100); //100

```

```kotlin Kotlin
val identify = Identify().set("number_of_clients", 10)
identify.add("number_of_clients", 5) //15
identify.add("annual_revenue", 100) //100

```
{% /code-group %}

#### Set multiple user properties

The `logEvent()` method lets you set user properties along with event logging. You can use `setUserProperties` as a shorthand to set multiple user properties at one time.

This method wraps `Identify.set`.

{% code-group %}
```java Java
JSONObject userProperties = new JSONObject();
try {
 userProperties.put("team", "red").put("favorite_food", "cabbage");
} catch (JSONException e) {
 e.printStackTrace();
 System.err.println("Invalid JSON");
}
client.setUserProperties(userProperties);
client.logEvent("event name");

```

```kotlin Kotlin
val userProperties = JSONObject()
try {
 userProperties.put("team", "red").put("favorite_food", "cabbage")
} catch (e: JSONException) {
 e.printStackTrace()
 System.err.println("Invalid JSON")
}
client.setUserProperties(userProperties)
client.logEvent("event name")

```
{% /code-group %}

#### Arrays in user properties

You can use arrays as user properties. You can directly set arrays or use `append` to generate an array.

{% code-group %}
```java Java
JSONArray value1 = new JSONArray();
value1.put(1);
value1.put(2);
value1.put(3);

Identify identify = new Identify();
identify.set("array value", value1);

```

```kotlin Kotlin
val value1 = JSONArray()
value1.put(1)
value1.put(2)
value1.put(3)

val identify = Identify()
identify["array value"] = value1

```
{% /code-group %}

- `append` appends a value or values to a user property array.
- `prepend` prepends a value or values to a user property.

If the user property doesn't have a value set yet, the SDK initializes the property to an empty list before it adds the new values. If the user property has an existing value that isn't a list, the SDK converts the value into a list and adds the new value.

{% callout type="note" heading="" %}
`append` and `prepend` don't check for duplicates. Refer to `preInsert` and `postInsert` for that.
{% /callout %}

{% code-group %}
```java Java
String property1 = "array value";
JSONArray value1 = new JSONArray();
value1.put(1);
value1.put(2);
value1.put(3);
Identify identify = new Identify();
identify.append(property1, value1);
identify.prepend("float value", 0.625f);

```

```kotlin Kotlin
val property1 = "array value"
val value1 = JSONArray()
value1.put(1)
value1.put(2)
value1.put(3)
val identify = Identify()
identify.append(property1, value1)
identify.prepend("float value", 0.625f)

```
{% /code-group %}

- `preInsert` inserts a value or values to the front of a user property array if the values don't exist in the array yet.
- `postInsert` inserts a value or values to the end of a user property array if the values don't exist in the array yet.

If the user property doesn't exist, the SDK initializes the property to an empty list before it pre-inserts the new values. If the user property has an existing value, the SDK inserts nothing.

{% code-group %}
```java Java
String property1 = "array value";
double[] values = {1, 2, 4, 8};
Identify identify = new Identify();
identify.postInsert(property1, values);

// identify should ignore this since duplicate key
identify.postInsert(property1, 3.0);

```

```kotlin Kotlin
val property1 = "array value"
val values = doubleArrayOf(1.0, 2.0, 4.0, 8.0)
val identify = Identify()
identify.postInsert(property1, values)
identify.postInsert(property1, 3.0)

```
{% /code-group %}

### Clear user properties

`clearUserProperties` removes all the current user's user properties.
\
{% callout type="warning" heading="This action is irreversible" %}
If you clear user properties, Amplitude can't sync the user's user property values from before the wipe to any future events.
{% /callout %}

```java
client.clearUserProperties();

```

#### unset

`unset` unsets and removes a user property.

{% code-group %}
```java Java
Identify identify = new Identify().setOnce("favorite_food", "candy");
identify.unset("favorite_food");

```

```kotlin Kotlin
val identify = Identify().setOnce("favorite_food", "candy")
identify.unset("favorite_food")

```
{% /code-group %}

### Set user groups

Amplitude supports assigning users to groups and running queries, such as Count by Distinct, on those groups. If at least one member of the group has performed the specific event, then the count includes the group.

For example, you want to group your users based on what organization they're in by using an 'orgId'. Joe is in 'orgId' '10', and Sue is in 'orgId' '15'. Sue and Joe both perform a certain event. You can query their organizations in the Event Segmentation Chart.

When you set groups, define a `groupType` and `groupName`. In the previous example, 'orgId' is the `groupType` and '10' and '15' are the values for `groupName`. Another example of a `groupType` could be 'sport' with `groupName` values like 'tennis' and 'baseball'.

Setting a group also sets the `groupType:groupName` as a user property, overwrites any existing `groupName` value set for that user's `groupType`, and overwrites the corresponding user property value. `groupType` is a string. `groupName` can be either a string or an array of strings to indicate that a user is in multiple groups.

{% callout type="example" heading="" %}
If Joe is in 'orgId' '10' and '16', then the `groupName` would be '["10", "16"]'. Here is what your code might look like:

```java
Amplitude.getInstance().setGroup("orgID", new JSONArray().put("10").put("16")); // list values
```

{% /callout %}

You can also use `logEvent` to set event-level groups. The group designation only applies for the specific event you log and doesn't persist on the user unless you explicitly set it with `setGroup`:

```java
JSONObject eventProperties = new JSONObject().put("key", "value");
JSONObject groups = new JSONObject().put("orgId", 10);

Amplitude.getInstance().logEvent("initialize_game", eventProperties, groups);
```

### Group identify

Use the Group Identify API to set or update the properties of particular groups. Keep these considerations in mind:

- Updates affect only future events, and don't update historical events.
- You can track up to 5 unique group types and 10 total groups.

The `groupIdentify` method accepts a group type string parameter, a group name object parameter, and an Identify object that the SDK applies to the group.

```java
String groupType = "plan";
Object groupName = "enterprise";

Identify identify = new Identify().set("key", "value");
Amplitude.getInstance().groupIdentify(groupType, groupName, identify);
```

You can supply an optional `outOfSession` boolean input as the fourth argument to `groupIdentify`.

### Track revenue

Amplitude can track revenue that a user generates. Amplitude tracks revenue through distinct revenue objects, which have special fields that Amplitude's Event Segmentation and Revenue LTV charts use.

This lets Amplitude automatically display data relevant to revenue in the platform.

To track revenue from a user, call `logRevenueV2` each time a user generates revenue.

{% code-group %}
```java Java
Revenue revenue = new Revenue().setProductId("com.company.productId").setPrice(3.99).setQuantity(3);
client.logRevenueV2(revenue);
```

```kotlin Kotlin
val revenue = Revenue().setProductId("com.company.productId").setPrice(3.99).setQuantity(3)
client.logRevenueV2(revenue)
```
{% /code-group %}

Revenue objects support the following special properties, and user-defined properties through the `eventProperties` field.

| Name               | Description                                                                                                                                                                      |
| ------------------ | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `productId`        | Optional. String. An identifier for the product. Amplitude recommends something like the "Google Play Store product ID". Defaults to `null`.                                     |
| `quantity`         | Required. Integer. The quantity of products purchased. Note: revenue = quantity \* price. Defaults to 1.                                                                         |
| `price`            | Required. Double. The price of the products purchased. This can be negative to track revenue lost, like refunds or costs. Note: revenue = quantity \* price. Defaults to `null`. |
| `revenueType`      | Optional, but required for revenue verification. String. The revenue type. For example: tax, refund, income. Defaults to `null`.                                                 |
| `receipt`          | Optional. String. The revenue type. For example: tax, refund, income. Defaults to `null`                                                                                         |
| `receiptSignature` | Optional, but required for revenue verification. The revenue type. For example: tax, refund, income. Defaults to `null`.                                                         |
| `eventProperties`  | Optional. JSONObject. An object of event properties to include in the revenue event. Defaults to `null`.                                                                         |

{% callout type="note" heading="" %}
Amplitude doesn't support currency conversion. Normalize all revenue data to your currency of choice before you send it.
{% /callout %}

### Revenue verification

The `logRevenue` method also supports revenue validation.

By default, revenue events recorded on the Android SDK appear in Amplitude as [Amplitude] Revenue (Unverified) events. To enable revenue verification, copy your Google Play License Public Key into the *Sources & Destinations* section of your project in Amplitude.

You must put in a key for every single project in Amplitude where you want Amplitude to verify revenue.

For more information, refer to the class specification for the [Purchase class](https://developer.android.com/reference/com/android/billingclient/api/Purchase).

{% code-group %}
```java Java: AIDL
// For AIDL (old deprecated library)

Intent data = ...;

String purchaseData = data.getStringExtra("PURCHASE_DATA");
String dataSignature = data.getStringExtra("DATA_SIGNATURE");

Revenue revenue = new Revenue().setProductId("com.company.productId").setQuantity(1);
revenue.setPrice(3.99).setReceipt(purchaseData, dataSignature);

client.logRevenueV2(revenue);
```

```java Java: Google Play Billing Library
//For Google Play Billing Library
public class MyBillingImpl implements PurchasesUpdatedListener {
    private BillingClient billingClient;
    //...

    public void initialize() {
        billingClient = BillingClient.newBuilder(activity).setListener(this).build();
        billingClient.startConnection(new BillingClientStateListener() {
            @Override
            public void onBillingSetupFinished(BillingResult billingResult) {
                // Logic from ServiceConnection.onServiceConnected should be moved here.
            }

            @Override
            public void onBillingServiceDisconnected() {
                // Logic from ServiceConnection.onServiceDisconnected should be moved here.
            }
        });
    }

    @Override
    public void onPurchasesUpdated(
        @BillingResponse int responseCode, @Nullable List<Purchase> purchases) {
        //Here is the important part.
        for (Purchase purchase: purchases) {
          Revenue revenue = new Revenue()
            .setProductId("com.company.productId")
            .setQuantity(1)
            .setPrice(price);
          revenue.setReceipt(purchase.getOriginalJson(), purchase.getSignature());
          client.logRevenueV2(revenue);
        }
    }
}
```

```kotlin Kotlin: AIDL
// For AIDL (old deprecated library)

Intent data = ...

val purchaseData: String = data.getStringExtra("PURCHASE_DATA")
val dataSignature: String = data.getStringExtra("DATA_SIGNATURE")

val revenue = Revenue().setProductId("com.company.productId").setQuantity(1)
revenue.setPrice(3.99).setReceipt(purchaseData, dataSignature)

client.logRevenueV2(revenue)
```

```kotlin Kotlin: Google Play Billing
class MyBillingImpl(private var billingClient: BillingClient) : PurchasesUpdatedListener {

    init {
        billingClient = BillingClient.newBuilder(activity).setListener(this).build()
        billingClient.startConnection(object : BillingClientStateListener {
            override fun onBillingSetupFinished(billingResult: BillingResult?) {
                // Logic from ServiceConnection.onServiceConnected should be moved here.
            }

            override fun onBillingServiceDisconnected() {
                // Logic from ServiceConnection.onServiceDisconnected should be moved here.
            }
        })
    }

    override fun onPurchasesUpdated(
        billingResult: BillingResult?,
        purchases: MutableList<Purchase>?
    ) {
        // Logic from onActivityResult should be moved here.
        for (Purchase purchase: purchases) {
          Revenue revenue = new Revenue()
            .setProductId("com.company.productId")
            .setQuantity(1)
            .setPrice(price);
          revenue.setReceipt(purchase.getOriginalJson(), purchase.getSignature());
          client.logRevenueV2(revenue);
        }
    }
}
```
{% /code-group %}

### Amazon store revenue verification

For purchases on the Amazon store, first set up Amazon as a data source in Amplitude.

1. In Amplitude, navigate to the *Data Sources* page.
2. Select **I want to import data into Amplitude**, then select **Amazon**.
3. Paste your Amazon Developer Shared Secret in the box and save.

After a successful purchase, send the purchase token (for Amazon IAP 2.0 use receipt ID) as the `receipt` and the User ID as the `receiptSignature`:

```java
// for a purchase request onActivityResult
String purchaseToken = purchaseResponse.getReceipt();
String userId = getUserIdResponse.getUserId();

Revenue revenue = new Revenue().setProductId("com.company.productId").setQuantity(1);
revenue.setPrice(3.99).setReceipt(purchaseToken, userId);

client.logRevenueV2(revenue);
```

## Debugging

Verify that the configuration and payload are correct, and check for any suspicious debug messages during debugging. If everything appears correct, check the value of `eventUploadThreshold` or `eventUploadPeriodMillis`. The SDK queues events and sends them in batches by default, so it doesn't dispatch them to the server immediately. Wait for the SDK to send the events to the server before you check for them in the charts.

### Log

Set the log level to debug to collect useful information during debugging.

### Callback

Set the `setLogCallback` to collect error messages from the SDK in a production environment.

## User sessions

A session on Android is a period of time that a user has the app in the foreground.

Amplitude groups events together by session. Events that you log within the same session share the same `session_id`. The SDK handles sessions automatically, so you don't have to manually call `startSession()` or `endSession()`.

You can adjust the time window for which the SDK extends sessions.

```java
client.setMinTimeBetweenSessionsMillis(10000); //Must be a 'long'; 10 seconds
```

By default, the SDK doesn't send '[Amplitude] Start Session' and '[Amplitude] End Session' events. Even though the SDK doesn't send these events, it still tracks sessions through `session_id`.

To enable those session events, add this line before you initialize the SDK.

```java
Amplitude.getInstance().trackSessionEvents(true);
```

You can also log events as out-of-session. Out-of-session events have a `session_id` of `-1` and aren't part of the current session, meaning they don't extend the current session.

This might be useful when you log events triggered by push notifications, for example. You can log events as out-of-session by setting the input parameter `outOfSession` to true when you call `logEvent()`.

```java
JSONObject eventProperties = //...

//This event is now out of session
client.logEvent("event type", eventProperties, true);
```

You can also log identify events as out-of-session. This approach is useful when you update user properties in the background and don't want to start a new session. To log identify events as out-of-session, set the input parameter `outOfSession` to true when you call `identify()`.

```java
Identify identify = new Identify().set("key", "value");
Amplitude.getInstance().identify(identify, true);
```

You may also manually start a new session with its own ID.

```java
long sessionId = ...;
client.startNewSessionIfNeeded(sessionId);
```

You can use the helper method `getSessionId` to get the value of the current `sessionId`.

```java
long sessionId = Amplitude.getInstance().getSessionId();
```

{% callout type="note" heading="" %}
For Android API level 14 and higher, the SDK creates a new session when the app returns to the foreground after being in the background for five or more minutes, or after the last event was logged (whichever occurred last).

Otherwise, the background event you log is part of the current session.

You can define your own session expiration time by calling `setMinTimeBetweenSessionsMillis(timeout)`, where the timeout input is in milliseconds.

For Android API level 13 and below, foreground tracking isn't available, so the SDK automatically starts a new session when you log an event 30 minutes or more after the last logged event. If you log another event within 30 minutes, the SDK extends the current session. You can define your own session expiration time by calling `setSessionTimeoutMillis(timeout)`, where the timeout input is in milliseconds. `enableForegroundTracking(getApplication)` is still safe to call for Android API level 13 and below, even though it isn't available.
{% /callout %}

## Set a custom user ID

If your app has its own login system that you want to track users with, you can call `setUserId` at any time.

```java
client.setUserId("USER_ID");
```

You can also add the User ID as an argument to the init call.

```java
client.initialize(this, "API_KEY", "USER_ID");

```

Don't assign users a user ID that could change, because each unique user ID is a unique user in Amplitude.

## Log level

You can control the level of logs that print to the developer console.

- 'INFO': Shows informative messages about events.
- 'WARN': Shows error messages and warnings. This level logs issues that might be a problem and cause some oddities in the data. For example, this level displays a warning for properties with null values.
- 'ERROR': Shows error messages only.
- 'DISABLE': Suppresses all log messages.
- 'DEBUG': Shows error messages, warnings, and informative messages that may be useful for debugging.

Set the log level by calling `setLogLevel` with the level you want.

```java
Amplitude.getInstance().setLogLevel(log.DEBUG)

```

## Logged out and anonymous users

Amplitude merges user data, so Amplitude links any events associated with a known `userId` or `deviceId` to the existing user.

If a user logs out, Amplitude can merge that user's logged-out events to the user's record. You can change this behavior and log those events to an anonymous user instead.

To log events to an anonymous user:

1. Set the `userId` to null.
2. Generate a new `deviceId`.

Events that come from the current user or device appear as a new user in Amplitude. If you do this, you can't see that the two users were using the same device.

```java
client.setUserId(null);
client.regenerateDeviceId();

```

## Disable tracking

By default, the Android SDK tracks several user properties such as `carrier`, `city`, `country`, `ip_address`, `language`, and `platform`.
Use the `TrackingOptions` interface to customize and toggle individual fields.

To use the `TrackingOptions` interface, import the class.

```java
import com.amplitude.api.TrackingOptions;

```

Before you initialize the SDK with your apiKey, create a `TrackingOptions` instance with your configuration and set it on the SDK instance.

```java
TrackingOptions options = new TrackingOptions().disableCity().disableIpAddress().disableLatLng();
Amplitude.getInstance().setTrackingOptions(options);

```

You can control tracking for each field individually. Each field has a corresponding method (for example, `disableCountry`, `disableLanguage`).

| Method                 | Description                                                           |
| ---------------------- | --------------------------------------------------------------------- |
| `disableAdid()`        | Disable tracking of Google ADID                                       |
| `disableCarrier()`     | Disable tracking of device's carrier                                  |
| `disableCity()`        | Disable tracking of user's city                                       |
| `disableCountry()`     | Disable tracking of user's country                                    |
| `disableDeviceBrand()` | Disable tracking of device brand                                      |
| `disableDeviceModel()` | Disable tracking of device model                                      |
| `disableDma()`         | Disable tracking of user's designated market area (DMA).              |
| `disableIpAddress()`   | Disable tracking of user's IP address                                 |
| `disableLanguage()`    | Disable tracking of device's language                                 |
| `disableLatLng()`      | Disable tracking of user's current latitude and longitude coordinates |
| `disableOsName()`      | Disable tracking of device's OS Name                                  |
| `disableOsVersion()`   | Disable tracking of device's OS Version                               |
| `disablePlatform()`    | Disable tracking of device's platform                                 |
| `disableRegion()`      | Disable tracking of user's region.                                    |
| `disableVersionName()` | Disable tracking of your app's version name                           |

{% callout type="note" heading="" %}
Using `TrackingOptions` only prevents the SDK from tracking default properties on newly created projects, where you haven't yet sent data. If you have a project with existing data that you want to stop collecting the default properties for, get help in the [Amplitude Community](https://community.amplitude.com/?utm_source=devdocs&utm_medium=helpcontent&utm_campaign=devdocswebsite). Disabling tracking doesn't delete any existing data in your project.
{% /callout %}

## Carrier

Amplitude determines the user's mobile carrier using [Android's TelephonyManager](https://developer.android.com/reference/android/telephony/TelephonyManager#getNetworkOperatorName%28%29) `getNetworkOperatorName()`, which returns the current registered operator of the `tower`.

## COPPA control

You can enable or disable COPPA (Children's Online Privacy Protection Act) restrictions on IDFA, IDFV, city, IP address, and location tracking at one time. Apps that ask for information from children under 13 years of age must comply with COPPA.

```java
client.enableCoppaControl(); //Disables ADID, city, IP, and location tracking

```

## Advertiser ID

The Android Advertising ID is a unique identifier that the Google Play store provides. Because the ID is unique to every person and not just their devices, it's useful for mobile attribution. The Android Advertising ID is similar to the IDFA on iOS. [Mobile attribution](https://www.adjust.com/blog/mobile-ad-attribution-introduction-for-beginners/) is the attribution of an installation of a mobile app to its original source (such as ad campaign, app store search). Users can choose to disable the Advertising ID, and apps targeted to children can't track at all.

Follow these steps to use Android Ad ID.

{% callout type="warning" heading="Google ad ID and tracking" %}
As of April 1, 2022, Google lets users opt out of Ad ID tracking. Ad ID may return null or error. You can use an alternative ID called [App Set ID](#app-set-id), which is unique to every app install on a device. For details, refer to [Google's Ad ID announcement](https://support.google.com/googleplay/android-developer/answer/6048248?hl=en).
{% /callout %}

1. Add `play-services-ads-identifier` as a dependency.

   ```java
   dependencies {
   implementation 'com.google.android.gms:play-services-ads-identifier:18.0.1'
   }

   ```

2. `AD_MANAGER_APP` permission.

   If you use Google Mobile Ads SDK version 17.0.0 or higher, add `AD_MANAGER_APP` to `AndroidManifest.xml`.

   ```xml
   <manifest>
   <application>
   <meta-data
   android:name="com.google.android.gms.ads.AD_MANAGER_APP"
   android:value="true"/>
   </application>
   </manifest>

   ```

3. Add ProGuard exception.

   Amplitude Android SDK uses Java Reflection to use classes in Google Play Services. For Amplitude SDKs to work in your Android application, add these exceptions to `proguard.pro` for the classes from `play-services-ads`.

4. `AD_ID` permission.

   When you update your app's target to Android 13 or above, declare a Google Play services normal permission in the manifest file as follows if you want to use the ADID as a deviceId:

   ```xml
   <uses-permission android:name="com.google.android.gms.permission.AD_ID"/>

   ```

### Set advertising ID as device ID

After you set up the logic to fetch the advertising ID, you can use `useAdvertisingIdForDeviceId` to set it as the device ID.

```java
client.useAdvertisingIdForDeviceId();

```

## App set ID

App set ID is a unique identifier for each app install on a device. The user resets the App set ID manually when they uninstall the app, or the ID resets after 13 months of not opening the app. Google designed App set ID as a privacy-friendly alternative to Ad ID for users who want to opt out of stronger analytics.

To use App Set ID, follow these steps.

1. Add `play-services-appset` as a dependency. For versions earlier than 2.35.3, use `'com.google.android.gms:play-services-appset:16.0.0-alpha1'`

   ```java
   dependencies {
   implementation 'com.google.android.gms:play-services-appset:16.0.2'
   }

   ```

2. Set app set ID as Device ID.

   ```java
   client.useAppSetIdForDeviceId();

   ```

## Device ID lifecycle

The SDK initializes the device ID in the following order. The SDK sets the device ID to the first valid value encountered:

1. Device ID fetched from the SQLite database.
2. ADID if `useAdvertisingIdForDeviceId` is enabled and the required module is installed. Refer to [Advertiser ID](#advertiser-id).
3. App Set ID with an `S` appended if `useAppSetIdForDeviceId` is enabled and the required module is installed. Refer to [App Set ID](#app-set-id).
4. A randomly generated UUID with an `R` appended.

### One user with multiple devices

A single user may have multiple devices, each with a different device ID. To ensure coherence, set the user ID consistently across all these devices. Even though the device IDs differ, Amplitude can still merge them into a single Amplitude ID and identify them as a unique user.

### Transfer to a new device

Multiple devices may have the same device ID when a user switches to a new device. When users transition to a new device, they often transfer their applications along with other relevant data. The specific transferred content may vary depending on the application. In general, it includes databases and file directories associated with the app. However, the exact items included depend on the app's design and the choices that the developers make. If databases or file directories were backed up from one device to another, the device ID stored within them may still be present. As a result, if the SDK attempts to retrieve the device ID during initialization, different devices might end up using the same device ID.

### Get the device ID

You can use the helper method `getDeviceId()` to get the value of the current `deviceId`.

{% code-group %}
```java Java
String deviceId = client.getDeviceId();
```

```kotlin Kotlin
val deviceId = client.getDeviceId();
```
{% /code-group %}

### Custom device ID

You can assign a new device ID using `setDeviceId()`. When you set a custom device ID, make sure the value is sufficiently unique. Amplitude recommends using a UUID.

```java
client.setDeviceId("DEVICE-ID");
```

## Location tracking

Amplitude converts the IP of a user event into a location (GeoIP lookup) by default. An app's own tracking solution or user data may override this information.

{% callout type="note" heading="Location tracking in version v2.40.3+" %}
As of version v2.40.3, the SDK disables location tracking by default. Call `enableLocationListening()` to track location data. When enabled, Amplitude uses Android location services (if available) to add specific coordinates (longitude and latitude) to logged events. Call `disableLocationListening()` at any time to disable location tracking.
{% /callout %}

```java
client.enableLocationListening();
client.disableLocationListening();
```

{% callout type="note" heading="Proguard obfuscation" %}
If you use ProGuard obfuscation, add the following exception to the file:
`-keep class com.google.android.gms.common.** { *; }`
{% /callout %}

## Opt users out of tracking

Users may wish to opt out of tracking entirely, which means Amplitude won't track any of their events or browsing history. `setOptOut` provides a way to fulfill a user's requests for privacy.

```java
client.setOptOut(true); //Disables all tracking of events for this user
```

## Push notification events

Don't send push notification events client-side through the Android SDK. Because a user must open the app to initialize the Amplitude SDK to send the event, the SDK doesn't send events to Amplitude until the next time the user opens the app. This can cause data delays.

You can use [mobile marketing automation partners](https://amplitude.com/integrations?category=mobile-marketing-automation) or the [HTTP API V2](https://developers.amplitude.com/docs/http-api-v2) to send push notification events to Amplitude.

## Event explorer

To use Event Explorer, you need either `deviceId` or `userId` to look up live events. This SDK provides a way to view them while using a debug build.

First, add the following code into your `AndroidManifest.xml`.

```xml
<activity
 android:name="com.amplitude.eventexplorer.EventExplorerInfoActivity"
 android:exported="true"
 android:screenOrientation="portrait"
 />

```

Second, add the following code in your root activity's `onCreate` life cycle.

```java
@Override
public void onCreate(Bundle savedInstanceState) {
 //...
 client.showEventExplorer(this);
 //...
}
```

## Dynamic configuration

The Android SDK lets you configure your apps to use dynamic configuration. This feature finds the best server URL automatically based on app users' location.

- If you have your own proxy server and use the `setServerUrl` API, leave dynamic configuration off.
- If you have users in China Mainland, then Amplitude recommends using dynamic configuration.
- By default, this feature returns the server URL of Amplitude's US servers. If you need to send data to Amplitude's EU servers, use `setServerZone` to set it to EU zone.

To use dynamic configuration, set `setUseDynamicConfig` to `true`.

```java
client.setUseDynamicConfig(true);
```

## SSL pinning

SSL Pinning is a technique used on the client side to avoid man-in-the-middle attacks by validating the server certificates again after SSL handshaking. Only use SSL pinning if you have a specific reason to do so. Contact Support before you ship any products with SSL pinning enabled.

To use SSL Pinning in the Android SDK, use the class `PinnedAmplitudeClient` instead of `AmplitudeClient` to turn it on.

## Set log callback

The Amplitude Android SDK lets the app set a callback (version 2.32.2+). Create a class and set the callback to help collect error messages from the SDK in a production environment.

```java
class SampleLogCallback implements AmplitudeLogCallback {
 @Override
 public void onError(String tag, String message) {
 // handling of error message
 }
}
SampleLogCallback callback = new SampleLogCallback();
client.setLogCallback(callback);
```

## Offline mode

The Amplitude SDK supports offline usage through the `setOffline(isOffline)` method. By default, the SDK disables offline mode.

When offline mode is enabled, the SDK saves events to local storage but doesn't send them to the Amplitude servers.

When offline mode is disabled, the SDK sends any pending events to Amplitude's servers immediately.

To limit the permissions the SDK requires, the SDK doesn't automatically detect network connectivity. Instead, you must manually call `setOffline()` to enable or disable offline mode.

```java
client.setOffline(true); // enables offline mode
client.setOffline(false); // disables offline mode
```

## Middleware

Middleware lets you extend Amplitude by running a sequence of custom code on every event.
This pattern is flexible, and you can use it to support event enrichment, transformation, filtering, routing to third-party destinations, and more.

Each middleware is an interface with a run method:

```java
void run(MiddlewarePayload payload, MiddlewareNext next);

```

The `payload` contains the `event` and an optional `extra` that lets you pass custom data to your own middleware implementations.

To invoke the next middleware in the queue, use the `next` function. You must call `next.run(payload)` to continue the middleware chain. If a middleware doesn't call `next`, then event processing stops after the current middleware completes.

Add middleware to Amplitude through `client.addEventMiddleware`. You can add as many middleware as you like. Each middleware runs in the order in which you add it.
