Device Tier Targeting

What is Device Tier Targeting?

Device Tier Targeting allows you to deliver different versions (e.g. resolutions, etc.) of the same asset to devices based on their hardware. For example, you may choose to deliver low resolution assets to lower end devices to improve performance, and deliver high resolution assets to higher end devices to improve graphic quality - all without incurring any increase in overall game size by only delivering the necessary assets to users' devices. This builds upon the concept of asset packs in Play Asset Delivery. As you'll see below, you have the power to define the tier criteria (for now based on RAM, specific device models or available system features), and can use up to 5 tiers.

Like Play Asset Delivery, Device Tier Targeting supports API 16 (Jelly Bean 4.1) and above, however on devices API 19 (KitKat 4.4.X) and lower the default tier is delivered regardless of the device build.

Developer Journey

At a high level, to integrate DTT into your existing game, you will need to take the following steps:

  1. Integrate Device Tier Targeting (and by extension, Play Asset Delivery) within your game
    • Integrate Play Asset Delivery into your game (if you haven't already done so)
    • Divide your assets into asset packs
    • Package your code and assets together for the final Android App Bundle artifact you will upload to Play.
  2. Create your Device Tier Targeting configuration so that Play knows how to deliver your assets to user devices.
    • Set up the Google Play Developer API (if not already completed), which is what you'll use to send the DTT configs to Play.
    • Go through the steps to create the DTT config.
  3. Upload your AAB to Play, and test to make sure everything is configured correctly

You will notice in the first section, there will be forks in the guide depending on the build system you use. The system you choose depends on the engine you're using, and your existing setup.

  • Gradle (recommended for Java and Native games): For games built using Gradle, follow these steps to configure the build system to build out your AAB with DTT support.
    • If you export your game to Gradle then finish your build there, we recommend following these instructions (ex. Unity games exported to Gradle))
  • Unity Plugin: We will provide you with unity packages to import into your Unity project, which will allow you to configure and build your AAB with DTT support.

Setting up Device Tier Targeting within your app

Integrating Play Asset Delivery into your game (if not already completed)

Play Asset Delivery (PAD) allows you to dynamically deliver your game's assets at install time or runtime, and you can read an overview about it here. With Device Tier Targeting, Play will deliver asset packs based on the device tier configurations you prescribe for different tiers of devices. It is recommended to follow the guidance below and integrate PAD into your game (i.e. create asset packs, implement retrieval in your game), and then modify the project code to enable Device Tier Targeting.

Gradle

For games built with Gradle, use these instructions for building your asset packs with Gradle, then follow the instructions for integrating asset pack retrieval within your game:

Unity

For games built in Unity, you will configure your asset packs using the AssetPackConfig class, as outlined in these instructions.

Creating device tier specific directories

If using Gradle

You will now split up your assets between the N tiers (max 5) you will define later on. Create your DTT directories by taking the existing asset bundle directories created in the last step, and post fixing the appropriate folder (as described below) with #tier_0, #tier_1, #tier_2, etc. When using the asset packs in your game - you will not need to address folders by postfix (in other words, the postfix is automatically stripped during the build process).

After the previous step, this might look like:

...
.../level1/src/main/assets/character-textures#tier_2/
.../level1/src/main/assets/character-textures#tier_1/
.../level1/src/main/assets/character-textures#tier_0/
...

When you access the files under the folder, you can just use the same path without post fixing, (in this example - I would reference as level1/assets/character-textures/ without any postfixes).

If using Unity

To add an asset pack that uses DTT, you can use the below AssetPackConfig methods:

/// Package the specified raw assets in the specified folders,
/// keyed by DeviceTier, in an AssetPack with the specified delivery mode.
public void AddAssetsFolders(
    string assetPackName,
    IDictionary<DeviceTier, string> deviceTierToAssetPackDirectoryPath,
    AssetPackDeliveryMode deliveryMode)

/// Package the specified AssetBundle files, which vary only by DeviceTier,
/// in an AssetPack with the specified delivery mode.
public void AddAssetBundles(
    IDictionary<DeviceTier, string> deviceTierToAssetBundleFilePath,
    AssetPackDeliveryMode deliveryMode)

For example, let's say you had three AssetBundles for your character at varying levels of detail.

To map these AssetBundles to their corresponding device tier, use the following snippet.

var assetPackConfig = new AssetPackConfig();
var tiers = new Dictionary<DeviceTier, string>
{
    {0, "Assets/LowPoly/Character"},
    {1, "Assets/Mid/Character"},
    {2, "Assets/Detailed/Character"}
};
assetPackConfig.AddAssetBundles(tiers, AssetPackDeliveryMode.OnDemand);

Building the Android App Bundle

Gradle

In your project's build.gradle file, configure your dependencies to have the versions below (or higher) for Android Gradle plugin and bundletool:

buildscript {
  dependencies {
    classpath 'com.android.tools.build:gradle:4.2.0'
    classpath "com.android.tools.build:bundletool:1.7.1"
    ...
  }
  ...
}

You will also need to update your gradle version to 6.7.1 or above. You can update this in gradle/wrapper/gradle-wrapper.properties within your project.

distributionUrl=https://services.gradle.org/distributions/gradle-6.7.1-all.zip

Finally, you will need to use the Play Asset Delivery Library; if you are still using the monolithic Play Core Library, update it to 1.8.3 or above. We recommend switching to the Play Asset Delivery Library and updating to the latest version if possible.

dependencies {
  implementation 'com.google.android.play:asset-delivery:2.0.1'
  ...
}

In the main app module's build.gradle file, enable the DTT split:

android {
  bundle {
    deviceTier {
      enableSplit true
    }
    ...
  }
  ...
}

Finally, you can build your Android App Bundle (AAB).

Bundletool

Build your bundle with bundletool, and while at the step to customize your AAB, add the following to your BundleConfig.pb file.

{
  ...
  "optimizations": {
    "splitsConfig": {
      "splitDimension": [
      ...
      {
        "value": "DEVICE_TIER",
        "negate": false,
        "suffixStripping": {
          "enabled": true,
        }
      }],
    }
  }
}

Unity

Once you've configured your AssetPackConfig to include your DTT packs, you can pass that config into one of the below methods to build your AAB:

// Configures the build system to use the newly created assetPackConfig when
// calling Google > Build and Run or Google > Build Android App
Bundle.AssetPackConfigSerializer.SaveConfig(assetPackConfig);
// Alternatively, use BundleTool.BuildBundle to build an App Bundle from script
BuildBundle(new buildPlayerOptions(), assetPackConfig)

Local Testing

Before moving on, it is recommended to locally test your app bundle to make sure everything is set up correctly. Using bundletool (1.8.0 or above), you locally build and test your app, explicitly specifying the correct device tier. You will first use build-apks to generate a set of .apks files, and then deploy your app to a connected device using install-apks. You can also specify which tier you'd like installed via the device-tier flag. You can find more info on this method of local testing here (please note that this page hasn't yet been updated for DTT and is thus missing the device-tier flag).

bundletool build-apks --bundle=/path/to/app.aab --output=/path/to/app.apks --local-testing
bundletool install-apks --apks=/path/to/app.apks --device-tier=1

Alternatively: You can also use extract-apks to extract a set of APKs for a specific device. Using get-device-spec along with specifying the device tier for this device, however, will not work in conjunction with the --local-testing flag, meaning you won't be able to test fast-follow or on-demand asset packs.

bundletool get-device-spec --output=/path/to/device-spec.json --device-tier=1
bundletool extract-apks --apks=/path/to/existing_APK_set.apks --output-dir=/path/to/device_specific_APK_set.apks --device-spec=/path/to/device-spec.json

Unity

The Google -> Build and Run menu option will build and run your game with the --local-testing flag enabled. However, it does not allow you to specify the device-tier passed into the install-apks command.

If you want to specify a device-tier other than 0, you should:

  1. Build the AAB using the Google -> Build Android App Bundle menu option.
  2. Follow the instructions in the previous section to run bundletool, build-apks, and install-apks on the built AAB.

Creating a Device Tier Configuration via Google Play Developer API

Getting started with the Google Play Developer API (if not already completed)

To configure Device Tier Targeting (e.g. Defining the requirements for each tier) you will need to use the Android Publisher API to upload your config to Google Play. You can read more about the API at the link above - there are a few steps you'll need to follow to get started:

  1. Create (if needed) and link your API project to your Google Play Console.
  2. Set-up an API Access Client.

You can find the API reference here - later on, if you choose to upload your build via the API, you will be using the Edits methods. Additionally, it is encouraged to review this page before using the API.

Using the Device Tier Configuration API

You can use the following API call to create your device tier configuration:

Create Device Tier Config

HTTP request POST https://androidpublisher.googleapis.com/androidpublisher/v3/applications/{packageName}/deviceTierConfigs
Path parameters N/A
Request Body Device Tier Config
Response Body Device Tier Config
Device Tier Config Object

The definition of device tiers comprises 2 steps:

  1. Define a set of device groups.
  2. Define your device tier set by assigning a level to your device groups.

A device group is a set of devices that match the selectors you define in the config.

Selectors can define requirements on device RAM and device model.

Groups are identified by a name that you choose; groups are allowed to overlap.

Next, you can define your device tier set by ranking the groups: each device tier is defined by its level and a device group.

If a device matches multiple tiers, it will be served the content for the highest matching tier.

  {
    device_groups: [
      {
        name: string,
        device_selectors: [
          {
            device_ram: {
              min_bytes: integer
              max_bytes: integer
            },
            included_device_ids: [
              {
                build_brand: string,
                build_device: string
              }
            ],
            excluded_device_ids: [
              {
                build_brand: string,
                build_device: string
              }
            ],
            required_system_features: [
              {
                name: string
              }
            ],
            forbidden_system_features: [
              {
                name: string
              }
            ]
          }
        ]
      }
    ],
    device_tier_set: {
      device_tiers: [
        {
          level: int,
          device_group_names: [string]
        }
      ]
    }
  }

Fields:

  • device_confid_id (integer): ID corresponding to this device tier configuration.
  • device_groups (object): Group definitions

    • name (string): Name of the device group (a string ID you define).
    • device_selectors (object): Device requirements for a device to belong to this group.
    • device_ram (object): Device RAM requirements
      • min_bytes (integer, inclusive): Minimum required RAM (in bytes)
      • max_bytes (integer, exclusive): Maximum required RAM (in bytes)
    • included_device_ids (object): Device models to be included in this selector (max of 10000 device_ids per group) A device needs to be in this list to match the selector. This is a necessary but not sufficient condition to match the full selector (see note above about combining requirements in a selector)
      • build_brand (string): Device manufacturer
      • build_device (string): Device model code
    • excluded_device_ids (object): Device models to be excluded in this selector (max of 10000 device_ids per group) A device from this list will not match the selector even if it matches all other requirements in the selector.
      • build_brand (string): Device manufacturer
      • build_device (string): Device model code
    • required_system_features (object): Features that a device needs to have to be included by this selector (max of 100 features per group). A device needs to have all system features in this list to match the selector. This is a necessary but not sufficient condition to match the full selector (see note above about combining requirements in a selector).

      System feature reference

      • name (string): A system feature
    • forbidden_system_features (object): Features that a device mustn't have to be included by this selector (max of 100 features per group). If a device has any of the system features in this list, it doesn't match the selector, even if it matches all other requirements in the selector.

      System feature reference

      • name (string): A system feature
  • device_tiers (object): Tier definitions

    • level (int): The level of the tier
    • group_name (string array): The name of the device groups that belong to this tier

You can find the correct formatting for the device manufacturer and model code by using the Device Catalog on the Google Play Console, by either:

  • Inspecting individual devices using the Device Catalog, and finding the manufacturer and model code in the locations as shown in the example below (For a Google Pixel 4a, the manufacturer is “Google” and the model code is “sunfish”)'

    pixel 4a page in the device catalog

    pixel 4a page in the device catalog

  • Downloading a CSV of supported devices, and using the Manufacturer and Model Code for the build_brand and build_device fields, respectively.

Here is an example config with 3 tiers - tier 2 uses device group high (which includes all devices over 7 GB and the Pixel 4), tier 1 uses device group medium (which includes all devices between 4-7 GB), and tier 0, which is implicitly defined as the catch-all group.

{
  device_groups: [
    {
      name: 'high',
      device_selectors: [
        {
          device_ram: {
            min_bytes: 7516192768
          },
        },
        {
          included_device_ids: [
            {
              build_brand: 'google',
              build_device: 'flame'
            }
          ],
        }
      ]
    },
    {
      name: 'medium',
      device_selectors: [
        {
          device_ram: {
            min_bytes: 4294967296,
            max_bytes: 7516192768
          },
        }
      ]
    }
  ],
  device_tier_set: {
    device_tiers: [
      {
        level: 1,
        device_group_names: [
          'medium'
        ]
      },
      {
        level: 2,
        device_group_names: [
          'high'
        ]
      }
    ]
  }
}

You can follow the instructions below for validating your Device Targeting Configuration before uploading it to Google Play.

Get Device Tier Config by ID

You can retrieve a specific device tier configuration by ID using the following call:

HTTP request GET https://androidpublisher.googleapis.com/androidpublisher/v3/applications/{packageName}/deviceTierConfigs/{deviceTierConfigId}
Path parameters N/A
Request Body N/A
Response Body Device Tier Config

Get list of Device Tier Configs

You can get the last 10 device tier configurations given the following call (or optimally specify a set of ten using the page_token query parameter):

HTTP request GET https://androidpublisher.googleapis.com/androidpublisher/v3/applications/{packageName}/deviceTierConfigs
Path parameters N/A
Query parameters page_token (optional) - Used to specify a specific group of 10 DTCs. This is useful if you have created more than 10 DTCs, and would like to see DTCs that were created before the most recent 10.
Request Body N/A
Response Body List of Device Tier Configs

page_token

Validating your Device Targeting Configuration

bundletool includes two commands that help you validate that your Device Targeting Configuration works as intended before uploading it to Play.

With bundletool print-device-targeting-config, you can validate that your JSON file is syntactically correct and visualize your device groups and tiers in a more readable format.

bundletool print-device-targeting-config --config=mydtc.json

With bundletool evaluate-device-targeting-config, you can evaluate what groups and tier would match a specific device. Either you connect your target device to your workstation and use the --connected-device flag; or you compile a JSON file with the device properties manually and provide it via the --device-properties flag.

bundletool evaluate-device-targeting-config --config=mydtc.json --connected-device
bundletool evaluate-device-targeting-config --config=mydtc.json --device-properties=deviceproperties.json

The device properties file should be a JSON file following the DeviceProperties protobuf structure. For example:

{
  "ram": 2057072640,
  "device_id": {
    "build_brand":"google",
    "build_device":"redfin"
  },
  "system_features": [
    {
      "name":"android.hardware.bluetooth"
    },
    {
      "name":"android.hardware.camera"
    }
  ]
}

Uploading your Android App Bundle to Google Play

Via API

You can use the Google Play Developer API to upload your Android App Bundle to Google Play, and link a specific Device Tier Targeting configuration to your build.

There is a general overview of the Edits methods here, along with deeper examples on releasing to the different tracks in Google Play Console (for the last link, you'll want to use the AAB-friendly APIs instead of the APK-friendly API, which are listed in the page). To specify the device tier config for your build, you will add the config id to the deviceTierConfigId query parameter while calling the edits.bundle.upload method, like this:

https://androidpublisher.googleapis.com/upload/androidpublisher/v3/applications/{packageName}/edits/{editId}/bundles?deviceTierConfigId="{deviceTierConfigId}

Via Google Play Console

You can follow the instructions here to upload your Android App Bundle. The latest DTC config will be applied to your App Bundle.

You can verify that your bundle was built correctly by going to App Bundle Explorer (with the correct build selected) > Delivery, and click on each asset pack. It should show that you have the N tiers you created. In this example, it shows that I have 3 tiers - 0, 1, and 2 for my asset pack main_asset.

asset pack with three tiers

Verifying the correct tier is being delivered

Use the following method to ensure only the correct tier is being delivered to the device

adb shell pm path {packageName}

You should see something like:

package:{...}/base.apk
package:{...}/split_config.en.apk
package:{...}/split_config.xxhdpi.apk
package:{...}/split_main_asset.apk
package:{...}/split_main_asset.config.tier_2.apk

Auxiliary

Quick Start using Curl

Below is an example (using the command line tool curl) of creating a new device tier config, and using the Edits api to create a new edit, upload a new AAB (associating it with a specific device tier config), set the track/release config, and commit the edit. (thus making the change public). Make sure to have the location of:

  • The key corresponding to your API client
  • The package name of your app

First, create a device tier config, and take note of the deviceTierConfigId you'll receive upon a successful call.

curl -H "$(oauth2l header --json $HOME/{apiKey} androidpublisher)" -XPOST -H "Content-Type: application/json" -d "{ device_groups: [ { name: 'high', device_selectors: [ { device_ram: { min_bytes: 7516192768 }, }, { included_device_ids: [ { build_brand: 'google', build_device: 'flame' } ], } ] }, { name: 'medium', device_selectors: [ { device_ram: { min_bytes: 4294967296, max_bytes: 7516192768 }, } ] } ], device_tier_set: { device_tiers: [ { level: 1, device_group_names: [ 'medium' ] }, { level: 2, device_group_names: [ 'high' ] } ] } }" https://androidpublisher.googleapis.com/androidpublisher/v3/applications/{packageName}/deviceTierConfigs

Start an edit - you will get an id and expiry time for the edit. Save the id for the following calls.

curl -H "$(oauth2l header --json $HOME/{apiKey} androidpublisher)" -XPOST https://androidpublisher.googleapis.com/androidpublisher/v3/applications/{packageName}/edits

Upload the AAB, specifying the device tier config as a query parameter - if the call is successful, you will see a version code, sha1 and sha256 of the build. Save the version code for the next call.

curl -H "$(oauth2l header --json $HOME/{apiKey} androidpublisher)" --data-binary @$HOME/{aabFile} -H "Content-Type: application/octet-stream" -XPOST https://androidpublisher.googleapis.com/upload/androidpublisher/v3/applications/{packageName}/edits/{editID}/bundles?deviceTierConfigId="{dttConfigID}"

Assign the AAB to the desired track (for testing, it is recommended to use the internal test track, but you can read more about the different tracks here), here we do a simple rollout without release notes, but you can read this page to learn more about how to staged rollouts, draft releases, and release notes. If this is your first time using the Publisher API, we recommend creating this as a draft release, and completing the release on your Google Play Console to ensure everything was configured correctly.

curl -H "$(oauth2l header --json $HOME/{apiKey} androidpublisher)" -XPUT -H "Content-Type: application/json" -d "{ releases: [{status: '{status}'</code>, <code><strong>versionCodes</strong></code>: <code>['{versionCode}']</code> <code><strong>}]}</strong></code>" <code>https://androidpublisher.googleapis.com/androidpublisher/v3/applications/{packageName}/edits/{editID}/tracks/{track}

Commit changes (proceed with caution, as this will make all changes go live on Play to the desired track)

curl -H "$(oauth2l header --json $HOME/{apiKey} androidpublisher)" -XPOST https://androidpublisher.googleapis.com/androidpublisher/v3/applications/{packageName}/edits/{editID}:commit