android / privacy-sandbox-samples

Apache License 2.0
130 stars 52 forks source link

Adding a custom audience fails when activation or expiration are not supplied #93

Closed guybashan closed 8 months ago

guybashan commented 8 months ago

We are using the following code in order to add a custom audience using fetchAndJoin:

                    FetchAndJoinCustomAudienceRequest request =
                            new FetchAndJoinCustomAudienceRequest.Builder(
                                    Uri.parse(urlFetchAndJoin)
                            )
                                    .setName(name)
                                    .setActivationTime(activationTime)
                                    .setExpirationTime(expirationTime)
                                    .build();

                    Log.i(TAG, "Executing Fetch and Join request");
                    customAudienceManager.fetchAndJoinCustomAudience(
                            request,
                            executor,
                            new OutcomeReceiver<Object, Exception>() {
                                @Override
                                public void onResult(Object ignoredResult) {
                                    Log.i(TAG, "Fetch and join executed successfully");
                                    completer.set(null);
                                }

                                @Override
                                public void onError(Exception error) {
                                    Log.e(TAG, "Failed while doing fetch and join", error);
                                    completer.setException(error);
                                }
                            });

                    return "fetchAndJoinCustomAudience";
                });

The code works fine when setting activation and expiration times. When removing one of these settings:

  setActivationTime(activationTime)
  setExpirationTime(expirationTime)

The command fails with the following exception:

2023-12-14 19:17:18.621  4263-4436  adservices.fledge       com.google.android.adservices.api    D  Error encountered in fetchCustomAudience execution
                                                                                                    java.io.InvalidObjectException: Fused custom audience is incomplete.
                                                                                                        at com.android.adservices.service.customaudience.FetchCustomAudienceImpl.lambda$validateResponse$1(FetchCustomAudienceImpl.java:378)
                                                                                                        at com.android.adservices.service.customaudience.FetchCustomAudienceImpl.$r8$lambda$FassehbCbH4RDs2Nc7fIx7_F_sw(FetchCustomAudienceImpl.java:0)
                                                                                                        at com.android.adservices.service.customaudience.FetchCustomAudienceImpl$$ExternalSyntheticLambda4.call(R8$$SyntheticClass:0)
                                                                                                        at com.google.common.util.concurrent.TrustedListenableFutureTask$TrustedFutureInterruptibleTask.runInterruptibly(TrustedListenableFutureTask.java:131)
                                                                                                        at com.google.common.util.concurrent.InterruptibleTask.run(InterruptibleTask.java:74)
                                                                                                        at com.google.common.util.concurrent.TrustedListenableFutureTask.run(TrustedListenableFutureTask.java:82)
                                                                                                        at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1145)
                                                                                                        at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:644)
                                                                                                        at java.lang.Thread.run(Thread.java:1012)

It seems like the above behavior is not as described in the documents:

The CustomAudienceFetchRequest object allows the API caller to define some information for the Custom Audience by using the optional properties shown in the example above. If specified these values cannot be overwritten by the buyer response received by the buyer.

cshmerling commented 8 months ago

Custom Audiences do have some require fields, defined in our developer guide. In this case, if you do not set activation or expiration time in the fetch request, they must be specified in the custom audience defined in the respsonse.

I will update our documentation to make this more clear.

guybashan commented 8 months ago

Hi @cshmerling, pls note:

So it seems like there is a mismatch between the docs and the behavior: activation and expiration seems to be mandatory on the client side and without it the request fails.

guybashan commented 8 months ago

I think this is still an open issue. So either the code should be adjusted or the documents..

cshmerling commented 8 months ago

Ah, I might have misunderstood the steps you ran- after you removed setting the expiration and activation times in the initial request, I thought the response did not have those fields as they would be ignored.

So you are saying that even if those fields are included in the response, you get this error? My only guess at that point would be the timestamps might not be valid, can you let me know what was included in the response?

guybashan commented 8 months ago

@cshmerling this is the use case:

The expected behavior: Use the server side activation and expiration values. The current behavior: We get the exception described above.

This is how the actual response looks like:

{
   "name":"running-shoes",
   "activationTimestamp":1700376470000,
   "expirationTimestamp":1702968470000,
   "user_bidding_signals":{
      "signal1":"value"
   },
   "trusted_bidding_data":{
      "trusted_bidding_uri":"https://audience.dsp.youappi.com/trusted",
      "trusted_bidding_keys":[
         "k1",
         "k2"
      ]
   },
   "bidding_logic_uri":"https://audience.dsp.youappi.com/logic",
   "daily_update_uri":"https://audience.dsp.youappi.com/daily",
   "ads":[
      {
         "render_uri":"https://audience.dsp.youappi.com/render",
         "metadata":{

         },
         "ad_filters":{
            "frequency_cap":{
               "win":[
                  {
                     "ad_counter_key":1,
                     "max_count":2,
                     "interval_in_seconds":60
                  }
               ],
               "view":[
                  {
                     "ad_counter_key":2,
                     "max_count":10,
                     "interval_in_seconds":3600
                  }
               ]
            },
            "app_install":{
               "package_names":[
                  "package.name.one",
                  "package.name.two"
               ]
            }
         }
      }
   ]
}
guybashan commented 8 months ago

@cshmerling please also note: when we add the activation/expiration to the Android App, all seems to work as expected. The reason this issues is specifically important for us is that we are a Bidder company working with MMPs. And we would like to make sure we have the capability of controlling some aspects of the fetchAndJoin operation from the server side.

cshmerling commented 8 months ago

Your expected behavior is correct- however, it looks like a mismatch between field names provided by the server and expected value. For activation/expiration time, please use

 "activation_time": 
 "expiration_time": 

Milliseconds are the correct unit.

Let me know if that doesn't work, and I can follow up on the issue.

guybashan commented 8 months ago

Hi @cshmerling , thanks for the correction. Things look better now. It would have been great if the docs contained the correct names.. :)