Guide for attributing out-of-market Android apps in China

  • Advertisers
  • Developers

Attribution of Android devices without GAID/Google Play Services. Best practice to viewing attribution data in AppsFlyer. Using AppsFlyer to aid in the detection of device manufacturer app-store hijacking. 

In China, there are several challenges that app owners face:

  • Google Play Services (GPS) is not active in the majority of Android devices. This means that the device has no GAID
  • The use of out-of-market stores creates opportunities for store hijacking by device manufacturers
  • Attribution servers are inaccessible or slow to respond
  • The need to manage and view attribution data from both Google Play store and out-of-market stores

Using AppsFlyer these challenges can be overcome by: 

  • Implementing attribution based on device IMEI as an alternative to GAID
  • Giving APKs a unique identifier to identify Android store hijacking
  • Configuring attribution links that are recognized within China

Prepare the App

Integrate AppsFlyer SDK and to prepare the APK using the instructions in the sections that follow.

Integrate the SDK in the app

Using IMEI instead of GAID

Google Play Services are usually not active on Android devices supplied in China. This means that there is no GAID. Using the device IMEI as a unique user identifier enables attribution recording.

The AppsFlyer SDK prevents access to the IMEI unless configured otherwise. The general assumption is that Google Play Services are active on the device and IMEI is not needed. In China, this is not the case.

When integrating the AppsFlyer SDK in your app, access to the IMEI needs to be enabled. Note once IMEI access is enabled, it remains enabled, even after Google Play Services are installed on the device.

To enable IMEI access:

  1. AppsFlyer Android SDK 4.8.18 or later is required
  2. Select one of the following methods. The methods are dependent on the Android API level used in your app.

Android API level up to 22

Use the following code example to integrate the SDK.


 public class AFApplication extends Application {
    private static final String AF_DEV_KEY = "";
    private static AFApplication instance;
    @Override
    public void onCreate() {
      //If you want to show debug log.
      AppsFlyerLib.getInstance().setDebugLog(true);
      //Use the two APIs below let AppsFlyer SDK collect IMEI & android id no matter the 
      //Google Play Service is exist or not.
      AppsFlyerLib.getInstance().setCollectIMEI(true);
      AppsFlyerLib.getInstance().setCollectAndroidID(true);  
      final AppsFlyerConversionListener conversionDataListener = new AppsFlyerConversionListener() {
          @Override
          public void onInstallConversionDataLoaded(Map<string, string=""> map) {} 
    	  @Override
          public void onInstallConversionFailure(String error) {}    
    	  @Override
          public void onAppOpenAttribution(Map<string, string=""> map) {}
          @Override
          public void onAttributionFailure(String s) {}
  	};
      AppsFlyerLib.getInstance().init(AF_DEV_KEY, conversionDataListener);
      AppsFlyerLib.getInstance().startTracking(context, AF_DEV_KEY);
  }
    @Override
    protected void attachBaseContext(Context base) {
        instance = this;
        super.attachBaseContext(base);
    }
    public static synchronized AFApplication getAppInstance() {
        return instance;
    }
}

Android API level 23 or later

Use the following code example to integrate the SDK.

Note: Android API level 23 or later requires user permission, using a prompt, to access the IMEI. This may result in the IMEI being retrieved after the activation of the StartTracking API.

            
Public class AFApplication extends Application {
   private static final String AF_DEV_KEY = "";
   private static AFApplication instance;
   @Override
   public void onCreate() {
      //If you want to show debug log.
      AppsFlyerLib.getInstance().setDebugLog(true);
      //Or use the two APIs below let AppsFlyer SDK collect IMEI & android id
      //no matter if the Google Play Service exists or not.
      AppsFlyerLib.getInstance().setCollectIMEI(true);
      AppsFlyerLib.getInstance().setCollectAndroidID(true);
      //Configure the min time between two sessions, the recommendation is 2 seconds。
      AppsFlyerLib.getInstance().setMinTimeBetweenSessions(2);
      final AppsFlyerConversionListener conversionDataListener = new 
AppsFlyerConversionListener() {
       @Override
       public void onInstallConversionDataLoaded(Map<string, string=""> map) {}
       @Override
       public void onInstallConversionFailure(String error) {}
       @Override
       public void onAppOpenAttribution(Map<string, string=""> map) {}
       @Override
       public void onAttributionFailure(String s) {}
     }
     AppsFlyerLib.getInstance().init(AF_DEV_KEY, conversionDataListener);
     AppsFlyerLib.getInstance().startTracking(context, AF_DEV_KEY);
   }  
   @Override
     protected void attachBaseContext(Context base) {
        instance = this;
        super.attachBaseContext(base);
    }
  public static synchronized AFApplication getAppInstance() {
        return instance;
    }
}

On the first launch of the app a READ_PHONE_STATE permission request is sent to the user in onResume() cal back of the MainActivity.

build.gradle in the app module

allprojects {
    repositories {
       ....
       maven { url 'https://jitpack.io'}
    }
}
dependencies {
    implementation 'com.github.tbruyelle:rxpermissions:0.10.2'
}
MainActivity
public class MainActivity extends AppCompactActivity {
    @Override
    protected void onResume() {
        super.onResume();
        final RxPermissions rxPermissions = new RxPermissions(this);
         if(RxPreference.Instance().getBoolean(RxPreference.KEY_PERMISSION_DIALOG_HAS_SHOW, false){
          return;
         }
         if(rxPermissions.isGranted(Manifest.permission.READ_PHONE_STATE)) {
            return;
         }
          RxPreference.Instance().putBoolean(RxPreference.KEY_PERMISSION_DIALOG_HAS_SHOW, true);
        rxPermissions.request(Manifest.permission.READ_PHONE_STATE)
                .subscribe(granted -> {
                    if (granted) {
                        AppsFlyerLib.getInstance().setCollectIMEI(true);
                        AppsFlyerLib.getInstance().setCollectAndroidID(true);
                        //NOTE: Here the report session API is reportTrackSession() not the startTracking()
                        AppsFlyerLib.getInstance().reportTrackSession(AFApplication.getAppInstance());
                    } else {
                    }
                });
    }
}

Define how attribution data displays in AppsFlyer 

Use one of the following methods to display attribution data in AppsFlyer:

  • (best practice) Single app means in AppsFlyer only one app displays. This app contains the data from both the out-of-markets (China) and Google Play stores attribution data.  This means that you the marketer see both domestic Chinese attribution data and foreign attribution data under one app in AppsFlyer. 
    Using this method does not hinder your ability to differentiate between the attribution source (app store, media source, etc.) or the ability to identify store hijacking.
  • Multiple apps means that the attribution data of each store is shown under a separate app. For example, each of the following is shown separately, Google Play Store, out-of-market store A, out-of-market-store B, etc.  To use this method see multiple apps.

Implementing the single app method

Marketers take note: Using the single app method enables you to see both domestic Chinese attribution data and foreign attribution data under one app in AppsFlyer. 

Prerequisite: The package name used in both Google Play Store and out-of-market stores are identical.

To Prepare a separate APK/manifest for each out-of-market store as follows:

  1. Add the following to the manifest to identify Chinese traffic:
    < meta-data android:name="CHANNEL" android:value="cn_market">
    Note: Parameters are case sensitive. The above uses the example channel name cn_market.
  2. Chose one of the following methods to identify the store:
    • Manifest method:  Add the following line to the AndoridManifest.xml file. The AF_STORE value needs to be unique for each store.
      <meta-data android:name="AF_STORE" android:value="example_store"/>
      --OR--
    • API method: Prepare a separate APK for each out-of-store market. Call the setOutOfStore API to set the AF_STORE value. Set a unique value for each store.
      AppsFlyerLib.getInstance().setOutOfStore("example_store")

The methods described set the parameter AF_STORE. The value of this parameter is shown in AppsFlyer raw data reports and in the install_app_store field. You can access the raw data* reports in AppsFlyer.

Note: * Raw data reports, an AppsFlyer premium report.

The methods described set the parameter AF_STORE. The value of this parameter is shown in AppsFlyer raw data reports in the install_app_store field. You can access raw data reports in AppsFlyer.

Additional attribution considerations

Workaround for stores without attribution link support

Many out-of-store markets in China also sell traffic, meaning they are also an ad platform. In some cases, these stores do not support the use of attribution links. The following workaround solutions can be used as an alternative:

  • install_app_store field: (recommended) When installation takes place without an attribution link, AppsFlyer attributes the installation to organic installs. By using the install_app_store field you can identify the actual source of the install. Note: The install_app_store field is set using the AF_STORE parameter described in the previous section.
  • Preinstall name in the manifest file. Using the preinstall name contained n the manifest. The disadvantage of this method is that where the preinstall APK is leaked to the market, attribution information will be incorrect. When using this method ensure that you have the appropriate commercial terms to protect your APK.

China domestic attribution links 

When preparing attribution links take into account the following:

China-based links

For media sources that only have domestic (Chinese) traffic, use the domain links detailed here. These links are recognized from within China and provide a superior user experience.

For regular attribution links use: https://app.aflink.com (aflink.com)

For OneLink attribution links use: hhtps://go.onelnk.com (onelnk.com) Note: Before using the onelnk.com domain contact your AppsFlyer CSM.

Note that in weChat only onelnk.com is whitelisted whereas onelink.me is blocked

Use af_r

Use af_r to make sure users are redirected to either an APK download URL or an APK store and not to Google Play Store.

Note: Frequently, China domestic integrated media sources have this parameter set as &redirect=false in the default attribution URL template. This means that redirection will be done by the ad platforms and not by AppsFlyer.

For more information about integrated media sources:
Android app promotion in China

Identifying store hijacking

In out-of-market scenarios, users first install the from the media source's (ad networks) out-of-market store. In the case of an installation hijack, a warning pop-up suggests the user installs/updates the app directly from the device (phone) manufacturer's app store. Where the user agrees, the user is re-directed to download from the manufacturer's app store and the APK downloaded. Meaning that the device manufacturer has hijacked the install.

The following example illustrates both a legitimate flow and a hijacked flow:

Legitimate flow

An app user clicks on an ad displayed by media-example, and either immediately downloads the app or is redirected to the legitimate-app-store to download the APP. In AppsFlyer the following attribution information is recorded:

  • Media source: media-example
  • Install app store: legitimate-app-store

Hijacked flow

An app user clicks on an ad displayed by media-example. When the download is initiated, a warning pop-up appears, suggesting that the user downloads the app from the device manufacturer's app store. If the user agrees, they are redirected to the manufacturer's store and download the app. In AppsFlyer the following attribution information is recorded:

  • Media source: media-example
  • Install-app-store: manufacturer-store

Identifying the hijack event in AppsFlyer

To identify the hijacked install you compare the media source and install-app-store fields. A mismatch between the expected install-app-store and the actual install-app-store indicate an installaton hijack.

Was this article helpful?
0 out of 0 found this helpful

Page Contents: