---
title: Collecting device profiles in Android
description: PingOne Advanced Identity Cloud PingAM Android
component: orchsdks
page_id: orchsdks:journey:use-cases/device-profiling/android-device-profile
canonical_url: https://developer.pingidentity.com/orchsdks/journey/use-cases/device-profiling/android-device-profile.html
revdate: Tue, 29 Oct 2025 14:22:33 +0100
keywords: ["Device", "Hardware", "Source Code", "Integration", "SDK", "Android"]
section_ids:
  android_device-profile_modules: Step 1. Installing modules
  android_device-profile_permissions: Step 2. Declaring permissions
  step_3_collecting_device_profiles: Step 3. Collecting device profiles
  adding_collectors_conditionally: Adding collectors conditionally
  creating_custom_collectors: Creating custom collectors
  integrating_with_advanced_identity_cloud_and_pingam_journeys: Integrating with Advanced Identity Cloud and PingAM journeys
---

# Collecting device profiles in Android

[icon: circle-check, set=far]PingOne Advanced Identity Cloud [icon: circle-check, set=far]PingAM [icon: android, set=fab]Android

The Device Profile module helps you to collect various attributes from an Android device.

It includes preconfigured collectors to collect attributes, and allows you to create your own collectors to suit your requirements.

## Step 1. Installing modules

For obtaining a device profile, you need this module:

* `device-profile`

To install the module into your Android app:

1. In the **Project** tree view of your Android Studio project, open the `build.gradle.kts` file.

2. In the `dependencies` section, add the `device-profile` module as a dependency:

   ```gradle
   dependencies {
       implementation("com.pingidentity.device:device-profile:2.0.0")
   }
   ```

## Step 2. Declaring permissions

The Device Profile module respects the Android permissions model, and certain collectors require that you declare the permissions needed.

To declare the permissions the app might request from the user:

1. Open the project's manifest file.

   For example, **app > manifests > AndroidManifest.xml**.

2. Add the relevant properties as a child of the `<manifest>` element:

   1. For `NetworkCollector`, add the `ACCESS_NETWORK_STATE` permission:

      ```xml
        <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
      ```

   2. For `LocationCollector`, optionally add the `ACCESS_FINE_LOCATION` and `ACCESS_COARSE_LOCATION` permissions:

      ```xml
        <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"/>
        <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/>
      ```

      |   |                                                                                                                                                                  |
      | - | ---------------------------------------------------------------------------------------------------------------------------------------------------------------- |
      |   | The `LocationCollector` automatically requests the relevant permission when gathering the data if needed, so adding the permissions to the manifest is optional. |

## Step 3. Collecting device profiles

To generate a device profile, create a list of the collectors you want to use, or specify `DefaultDeviceCollector` to use the default list, and then call the `collect()` method to create the device profile JSON:

Obtaining a device profile on an Android device

```kotlin
import com.pingidentity.device.profile.collector.DefaultDeviceCollector
import com.pingidentity.device.profile.collector.collect

suspend fun collectDeviceProfile() {

    // Specify which collectors to use
    val collectors = mutableListOf<DeviceCollector<*>>().apply {
        clear()
        add(PlatformCollector())
        add(HardwareCollector())
        add(NetworkCollector())
        add(TelephonyCollector())
        add(LocationCollector()) // Handles permissions automatically
    }

    // Or use the default list of collectors
    val collectors = mutableListOf<DeviceCollector<*>>().apply(DefaultDeviceCollector())

    // Collect device information
    val deviceProfile = collectors.collect()

    // Use the collected profile (JsonObject)
    println(deviceProfile.toString())
}
```

The **device-profile** module provides the following collectors:

**Included device profile collectors**

| Collector            | Attributes collected                                                                                                                                                                                                                                                                                                                                                                                                                                                       |
| -------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `PlatformCollector`  | Gathers platform and device identification information, such as the brand and model.Example data returned by the `PlatformCollector````json
{
  "platform": {
    platform = "android",
    version = 31,
    device = "pixel6",
    deviceName = "Pixel 6",
    model = "Pixel 6",
    brand = "google",
    locale = "en_US",
    timeZone = "America/New_York",
    jailBreakScore = 0.0
  }
}
```                                                                      |
| `HardwareCollector`  | Gathers information about the hardware, such as the CPU and display.Example data returned by the `HardwareCollector````json
{
  "hardware": {
    "hardware": "flame",
    "manufacturer": "Google",
    "storage": 64,
    "memory": 6144,
    "cpu": 8,
    "display": {
      "width": 1080,
      "height": 2280,
      "orientation": 0
    },
    "camera": {
      "numberOfCameras": 2
    }
  }
}
```                                                             |
| `NetworkCollector`   | Whether the device has any network connectivity.Example data returned by the `NetworkCollector````json
{
   "network": {
      "connected": true
   }
}
```                                                                                                                                                                                                                                                                                                                |
| `TelephonyCollector` | Collects information about the carriers the device uses, such as the carrier name and the country code.Example data returned by the `TelephonyCollector````json
{
   "telephony": {
      "networkCountryIso": "US",
      "carrierName": "Verizon"
   }
}
```                                                                                                                                                                                                             |
| `LocationCollector`  | Collects latitude and longitude coordinates from the device.- Automatically requests permissions when needed

- Uses a transparent activity for seamless permission flow

- Gracefully handles permission denials

- Includes a 30-second protection against timeouts

- Returns `null` if a location is unavailable or deniedExample data returned by the `LocationCollector````json
{
   "location": {
      "latitude": 37.2431,
      "longitude": 115.7930
   }
}
``` |
| `BluetoothCollector` | Whether the device has any bluetooth support.Example data returned by the `BluetoothCollector````json
{
    "bluetooth": {
        "supported": true
    }
}
```                                                                                                                                                                                                                                                                                                           |
| `BrowserCollector`   | Collects the user-agent string of the default browser on the device.Example data returned by the `BrowserCollector````json
{
    "browser": {
        "userAgent": "Mozilla/5.0 (Linux; Android 10; Pixel 4) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/83.0.4103.106 Mobile Safari/537.36"
    }
}
```                                                                                                                                                                 |

### Adding collectors conditionally

You can add collectors by checking that the user has granted the necessary permissions:

Checking permissions before adding collectors

```kotlin
fun createCollectors(context: Context): List<DeviceCollector<*>> {
    return mutableListOf<DeviceCollector<*>>().apply {
        // Always include basic collectors
        add(PlatformCollector())
        add(HardwareCollector())

        // Conditionally add collectors based on permissions
        if (ContextCompat.checkSelfPermission(context, ACCESS_NETWORK_STATE) == PERMISSION_GRANTED) {
            add(NetworkCollector())
        }

        // LocationCollector handles its own permissions
        add(LocationCollector())

        // Add based on device capabilities
        if (packageManager.hasSystemFeature(FEATURE_TELEPHONY)) {
            add(TelephonyCollector)
        }
    }
}
```

## Creating custom collectors

You can create custom device collectors to gather specific attributes depending on your requirements, or the hardware you will support.

Use the `DeviceCollector` class to create your custom collector, using one of the following patterns:

* Factory function

* Data class

* Implement the interface

Using a factory function to create a custom collector

```kotlin
val BatteryCollector = DeviceCollector<Map<String, String>>("battery") {
    mapOf(
        "level" to "100",
        "isCharging" to "true",
    )
}
```

Using a data class to create a custom collector

```kotlin
@Serializable // Ensure the data class is serializable
data class BatteryData(val level: Int, val isCharging: Boolean, val capacity: Int)

val BatteryCollector = DeviceCollector<BatteryData>("battery") {
    BatteryData(
        level = 100,
        isCharging = true,
        capacity = 4000,
    )
}
```

Implementing the interface to create a custom collector

```kotlin
class SecurityCollector : DeviceCollector<SecurityInfo> {
    override val key = "security"
    override val serializer = SecurityInfo.serializer()

    override suspend fun collect(): SecurityInfo {
        return SecurityInfo(
            isDeviceSecure = checkDeviceSecuritySettings(),
            hasScreenLock = checkScreenLockStatus(),
            biometricsAvailable = checkBiometricCapabilities()
        )
    }
}

@Serializable
data class SecurityInfo(
    val isDeviceSecure: Boolean,
    val hasScreenLock: Boolean,
    val biometricsAvailable: Boolean
)
```

## Integrating with Advanced Identity Cloud and PingAM journeys

You can use the Device Profile module to collect the data the [**Device Profile Collector node**](https://docs.pingidentity.com/auth-node-ref/latest/device-profile-collector.html) requires when used as part of a Advanced Identity Cloud or PingAM auth journey.

The Device Profile module provides the `DeviceProfileCallback` class. The class includes a `collect()` method, which collects the data and formats it ready for return to the server.

Preparing a device profile for the Device Profile Collector node

```kotlin
import com.pingidentity.device.profile.DeviceProfileCallback
import kotlinx.coroutines.runBlocking

// Collect with default settings - use DefaultDeviceCollector
val result = deviceProfileCallback.collect {
    collectors.apply(DefaultDeviceCollector())
}

result.onSuccess { profile ->
    // Submit to AIC service
    node = node.next()
    // Note: Some collectors may return null values if data is unavailable
}.onFailure { e ->
    // Handle collection errors
    Log.e("DeviceProfile", "Failed to collect profile", e)
}
```

The module formats the resulting device profile for consumption by the device profile collector node, and includes a device identifier:

Example device profile for the Device Profile Collector node

```json
{
  "identifier": "unique-device-id",
  "platform": {
    "platform": "android",
    "device": "flame",
    "deviceName": "Pixel 4"
  },
  "hardware": {
    "manufacturer": "Google",
    "storage": 64,
    "memory": 6144
  },
  "network": {
    "connected": true
  },
  "location": {
    "latitude": 37.2431,
    "longitude": 115.7930
  }
}
```

You can customize which collectors to use when integrating with the device profile collector node, and also the device identifier the profile includes.

The **Device Profile** module automatically uses the **Device ID** module by default to generate this identifier, but you can customize how it's generated. Learn about customizing the device identifier in [Customizing device identifiers on Android](android-device-ids.html).

Customizing a device profile for the Device Profile Collector node

```kotlin
val profile = deviceProfileCallback.collect {
   // Set a custom device identifier if needed
   deviceIdentifier = object : DeviceIdentifier {
       override val id: suspend () -> String = { "your-custom-device-id" }
   }

   // Adds collectors in a metadata block
   collectors {
      // Clear default collectors
      clear()
      // Add specific collectors
      add(PlatformCollector())
      add(HardwareCollector())
      add(NetworkCollector())
      add(LocationCollector()) // Will automatically handle permissions
   }
}

// The profile is now ready for submission to AIC services
```

The `deviceProfileCallback` class automatically formats the device profile for use with the Device Profile Collector node.
