Implement Topics API on Android

Setup

To implement the Topics API, you need to set up your development environment first. to perform the following setup steps:

  1. Use the latest Android Privacy Sandbox SDK to get the most up-to-date version of the privacy-preserving APIs.

  2. Add the following to your manifest:

    • Permission:Include the ACCESS_ADSERVICES_TOPICS permission to allow your app to access the Topics API:

       <uses-permission  
      android:name="android.permission.ACCESS_ADSERVICES_TOPICS"  
      /> 
      
    • Ad Services Configuration:Reference an Ad Services configuration file in the <application> element of your manifest.

       <property  
      android:name="android.adservices.AD_SERVICES_CONFIG"
      android:resource="@xml/ad_services_config"  
      /> 
      

      Specify the Ad Services XML resource referenced in the manifest, such as res/xml/ad_services_config.xml . Use either the allowAllToAccess attribute to grant access to all SDKs, or the allowSdksToAccess attribute to grant access to individual SDKs. Learn more about Ad Services permissions and SDK access control .

       <ad-services-config>  
      <topics  
      allowAllToAccess="true"/>
      </ad-services-config> 
      
  3. Enroll your ad tech with the Privacy Sandbox to call Topics API in your SDK. For testing locally, you can disable Topics enrollment check with the following commands:

     adb  
    shell  
    setprop  
    debug.adservices.disable_topics_enrollment_check  
     true 
     
    
  4. Enable access to the Topics API. By default, the Topics API is disabled. You need to enable it using ADB commands:

     adb  
    shell  
    device_config  
    put  
    adservices  
    ppapi_app_signature_allow_list  
     \"\*\" 
     
    
     adb  
    shell  
    setprop  
    debug.adservices.disable_topics_enrollment_check  
     true 
     
    
  5. Kick off your implementation by forking our Java or Kotlin version of the sample app to familiarize yourself with how to retrieve topics on a device.

Request a set of topics

The primary functionality of the Topics API resides in the getTopics() method inside of the TopicsManager object, as shown in this example:

Kotlin

  fun 
  
 getTopics 
 ( 
  
 getTopicsRequest 
 : 
  
 GetTopicsRequest 
 , 
  
 executor 
 : 
  
 Executor 
 , 
  
 callback 
 : 
  
 OutcomeReceiver<GetTopicsResponse 
 , 
  
 Exception 
>  
 ) 
  
 { 
  
 } 
 

Java

  public 
  
 void 
  
 getTopics 
  
 ( 
 @NonNull 
  
 GetTopicsRequest 
  
 getTopicsRequest 
 , 
  
 @NonNull 
  
 Executor 
  
 executor 
 , 
  
 @NonNull 
  
 OutcomeReceiver<GetTopicsResponse 
 , 
  
 Exception 
>  
 callback 
 ) 
 

To use this method, initialize the TopicsManager object and the parameters necessary to receive topics data. GetTopicsRequest passes needed information to retrieve Topics API data, including a flag to indicate whether the caller is going to act as an observer or not. When notacting as an observer, the getTopics call returns a topic from the previous epoch, but won't influence topics data for the following one. The OutcomeReceiver callback handles the result asynchronously. For example:

Kotlin

  private 
  
 fun 
  
 topicGetter 
 () 
  
 { 
  
 val 
  
 mContext 
  
 = 
  
 baseContext 
  
 val 
  
 mTopicsManager 
  
 = 
  
 mContext 
 . 
 getSystemService 
 ( 
 TopicsManager 
 :: 
 class 
 . 
 java 
 ) 
  
 val 
  
 mExecutor 
 : 
  
 Executor 
  
 = 
  
 Executors 
 . 
 newCachedThreadPool 
 () 
  
 val 
  
 shouldRecordObservation 
  
 = 
  
 false 
  
 val 
  
 mTopicsRequestBuilder 
 : 
  
 GetTopicsRequest 
 . 
 Builder 
  
 = 
  
 GetTopicsRequest 
 . 
 Builder 
 () 
  
 mTopicsRequestBuilder 
 . 
 setAdsSdkName 
 ( 
 baseContext 
 . 
 packageName 
 ) 
  
 mTopicsRequestBuilder 
 . 
 setShouldRecordObservation 
 ( 
 shouldRecordObservation 
 ) 
  
 mTopicsManager 
 . 
 getTopics 
 ( 
 mTopicsRequestBuilder 
 . 
 build 
 (), 
  
 mExecutor 
 , 
  
 mCallback 
  
 as 
  
 OutcomeReceiver<GetTopicsResponse 
 , 
  
 Exception 
> ) 
 } 
 private 
  
 var 
  
 mCallback 
 : 
  
 OutcomeReceiver<GetTopicsResponse 
 , 
  
 java 
 . 
 lang 
 . 
 Exception 
>  
 = 
 object 
  
 : 
  
 OutcomeReceiver<GetTopicsResponse 
 , 
  
 java 
 . 
 lang 
 . 
 Exception 
>  
 { 
  
 override 
  
 fun 
  
 onResult 
 ( 
 result 
 : 
  
 GetTopicsResponse 
 ) 
  
 { 
  
 // handle successful result 
  
 val 
  
 topicsResult 
  
 = 
  
 result 
 . 
 topics 
  
 for 
  
 ( 
 i 
  
 in 
  
 topicsResult 
 . 
 indices 
 ) 
  
 { 
  
 Log 
 . 
 i 
 ( 
 "Topic" 
 , 
  
 topicsResult 
 [ 
 i 
 ] 
 . 
 getTopicId 
 (). 
 toString 
 ()) 
  
 } 
  
 if 
  
 ( 
 topicsResult 
 . 
 size 
  
 == 
  
 0 
 ) 
  
 { 
  
 Log 
 . 
 i 
 ( 
 "Topic" 
 , 
  
 "Returned Empty" 
 ) 
  
 } 
  
 } 
  
 override 
  
 fun 
  
 onError 
 ( 
 error 
 : 
  
 java 
 . 
 lang 
 . 
 Exception 
 ) 
  
 { 
  
 // handle error 
  
 Log 
 . 
 i 
 ( 
 "Topic" 
 , 
  
 "Error, did not return successfully" 
 ) 
  
 } 
 } 
 

Java

  public 
  
 void 
  
 TopicGetter 
 () 
  
 { 
  
 @NonNull 
  
 Context 
  
 mContext 
  
 = 
  
 getBaseContext 
 (); 
  
 TopicsManager 
  
 mTopicsManager 
  
 = 
  
 mContext 
 . 
 getSystemService 
 ( 
 TopicsManager 
 . 
 class 
 ); 
  
 Executor 
  
 mExecutor 
  
 = 
  
 Executors 
 . 
 newCachedThreadPool 
 (); 
  
 boolean 
  
 shouldRecordObservation 
  
 = 
  
 false 
 ; 
  
 GetTopicsRequest 
 . 
 Builder 
  
 mTopicsRequestBuilder 
  
 = 
  
 new 
  
 GetTopicsRequest 
 . 
 Builder 
 (); 
  
 mTopicsRequestBuilder 
 . 
 setAdsSdkName 
 ( 
 getBaseContext 
 (). 
 getPackageName 
 ()); 
  
 mTopicsRequestBuilder 
 . 
 setShouldRecordObservation 
 ( 
 shouldRecordObservation 
 ); 
  
 mTopicsManager 
 . 
 getTopics 
 ( 
 mTopicsRequestBuilder 
 . 
 build 
 (), 
  
 mExecutor 
 , 
  
 mCallback 
 ); 
 } 
 OutcomeReceiver 
  
 mCallback 
  
 = 
  
 new 
  
 OutcomeReceiver<GetTopicsResponse 
 , 
  
 Exception 
> () 
  
 { 
  
 @Override 
  
 public 
  
 void 
  
 onResult 
 ( 
 @NonNull 
  
 GetTopicsResponse 
  
 result 
 ) 
  
 { 
  
 //Handle Successful Result 
  
 List<Topic> 
  
 topicsResult 
  
 = 
  
 result 
 . 
 getTopics 
 (); 
  
 for 
  
 ( 
 int 
  
 i 
  
 = 
  
 0 
 ; 
  
 i 
 < 
 topicsResult 
 . 
 size 
 (); 
  
 i 
 ++ 
 ) 
  
 { 
  
 Log 
 . 
 i 
 ( 
 "Topic" 
 , 
  
 topicsResult 
 . 
 get 
 ( 
 i 
 ). 
 getTopicId 
 (). 
 toString 
 ()); 
  
 } 
  
 if 
  
 ( 
 topicsResult 
 . 
 size 
 () 
  
 == 
  
 0 
 ) 
  
 { 
  
 Log 
 . 
 i 
 ( 
 "Topic" 
 , 
  
 "Returned Empty" 
 ); 
  
 } 
  
 } 
  
 @Override 
  
 public 
  
 void 
  
 onError 
 ( 
 @NonNull 
  
 Exception 
  
 error 
 ) 
  
 { 
  
 // Handle error 
  
 Log 
 . 
 i 
 ( 
 "Topic" 
 , 
  
 "Experienced an error, and did not return successfully" 
 ); 
  
 } 
 }; 
 

Once your setup is ready, you can make a call to receive a GetTopicsResponse as a result from the getTopics() method:

Kotlin

  mTopicsManager 
 . 
 getTopics 
 ( 
 mTopicsRequestBuilder 
 . 
 build 
 (), 
  
 mExecutor 
 , 
  
 mCallback 
  
 as 
  
 OutcomeReceiver<GetTopicsResponse 
 , 
  
 java 
 . 
 lang 
 . 
 Exception 
> ) 
 

Java

  mTopicsManager 
 . 
 getTopics 
 ( 
 mTopicsRequestBuilder 
 . 
 build 
 (), 
  
 mExecutor 
 , 
  
 mCallback 
 ); 
 

This invocation will provide a list of Topics objects containing ID values that correspond to topics in the open source taxonomy that are relevant to the user, or a relevant error. The topics will resemble this example:

 /Internet & Telecom/Text & Instant Messaging 

Refer to the taxonomy for a list of possible topics that can be returned. This taxonomy is open source and suggested changes can be filed using the feedback button at the top of this page.

Testing

The Topics API provides relevant and fresh topics based on app usage. This early version gives a preview of the API behaviors, and we will improve the quality of topics over future releases.

To get the fullest experience, we recommend a testing environment with multiple apps where you call getTopics() to see how topics are selected. The SDK Runtime and Privacy Preserving APIs Repository on GitHub contains a set of individual Android Studio projects to help you get started, including samples that demonstrate how to initialize and call the Topics API.

The topics calculation takes place at the end of an epoch. By default, each epoch is 7 days long, but you can modify this interval to get a result. This Android Debug Bridge shell command shortens the epoch length to 5 minutes:

 adb  
shell  
device_config  
put  
adservices  
topics_epoch_job_period_ms  
 300000 
 

You can confirm the topics_epoch_job_period_ms value with get :

 adb  
shell  
device_config  
get  
adservices  
topics_epoch_job_period_ms 

To manually trigger epoch computation, execute the following command:

 adb  
shell  
cmd  
jobscheduler  
run  
-f  
com.google.android.adservices.api  
 2 
 

In addition to using the sample app, there is a colab that you can use to test different combinations of app info against the topics classifier. Use this colab to view the kinds of results your app is likely to get when calling getTopics .

Encryption details

With the introduction of encryption, calls to GetTopics() will now generate a response with a list of EncryptedTopic objects. Decrypting these results will result in an object with the same JSON format of the previous Topic object.

Topics API supports one shot implementation of HPKE (Hybrid Public Key Encryption). We expect the enrolled caller to host a 32-bit public key on the public encryption URL endpoint provided during enrollment. These keys are expected to be Base64 encoded.

EncryptedTopic object's have three fields. The list of returned topics can be obtained by using the corresponding private key for the public key.

For development purposes, you can test the Topics API encryption by disabling the enrollment check. This would force the API to use the test public key for encrypting your responses. You can decrypt the encrypted topics using the corresponding private key.

Limitations

For a list of in-progress capabilities for the Topics API, refer to the release notes .

Your feedback is a crucial part of the Privacy Sandbox on Android. Let us know of any issues you find or ideas for improving Privacy Sandbox on Android.

Next steps

Learn how users and developers can manage and customize the Topics API to suit user's preferences and needs.
Understand how Topics works on Android and lear about the core steps of the API flow.

See also

Check out our resources to better understand the Topics API on Android.

Create a Mobile Website
View Site in Mobile | Classic
Share by: