Add new Source#getFilterData method to accept Trigger so that we can store duration from the source and trigger in the filter map.
Details:
- Also created a similar method for Source#getAggregatableAttributionSource
- Some minor improvements for FilterMap.
Bug: 295530374
Test: atest com.android.adservices.service.measurement.FilterMapTest
Change-Id: Iccf08f1aca62545ce3ee403cddb55fbe6048ea3f
diff --git a/adservices/service-core/java/com/android/adservices/service/measurement/FilterMap.java b/adservices/service-core/java/com/android/adservices/service/measurement/FilterMap.java
index b0c36c3..17974af 100644
--- a/adservices/service-core/java/com/android/adservices/service/measurement/FilterMap.java
+++ b/adservices/service-core/java/com/android/adservices/service/measurement/FilterMap.java
@@ -77,6 +77,22 @@
}
/**
+ * Returns the long value given the key. {@code key} must be present and the value kind must be
+ * {@link FilterValue.Kind#LONG_VALUE}.
+ */
+ public long getLongValue(String key) {
+ return mAttributionFilterMapWithLongValue.get(key).longValue();
+ }
+
+ /**
+ * Returns the string list value given the key. {@code key} must be present and the value kind
+ * must be {@link FilterValue.Kind#STRING_LIST_VALUE}.
+ */
+ public List<String> getStringListValue(String key) {
+ return mAttributionFilterMapWithLongValue.get(key).stringListValue();
+ }
+
+ /**
* Serializes the object into a {@link JSONObject}.
*
* @return serialized {@link JSONObject}.
@@ -148,6 +164,18 @@
return this;
}
+ /** Adds filter with long value. */
+ public Builder addLongValue(String key, long value) {
+ mBuilding.mAttributionFilterMapWithLongValue.put(key, FilterValue.ofLong(value));
+ return this;
+ }
+
+ /** Adds filter with string list value. */
+ public Builder addStringListValue(String key, List<String> value) {
+ mBuilding.mAttributionFilterMapWithLongValue.put(key, FilterValue.ofStringList(value));
+ return this;
+ }
+
/**
* See {@link FilterMap#getAttributionFilterMap()}.
*
diff --git a/adservices/service-core/java/com/android/adservices/service/measurement/Source.java b/adservices/service-core/java/com/android/adservices/service/measurement/Source.java
index 95b1565..d4029ba 100644
--- a/adservices/service-core/java/com/android/adservices/service/measurement/Source.java
+++ b/adservices/service-core/java/com/android/adservices/service/measurement/Source.java
@@ -49,6 +49,7 @@
import java.util.Objects;
import java.util.Optional;
import java.util.TreeMap;
+import java.util.concurrent.TimeUnit;
/**
* POJO for Source.
@@ -728,7 +729,10 @@
/**
* Returns the AggregatableAttributionSource object, which is constructed using the aggregate
* source string and aggregate filter data string in Source.
+ *
+ * @deprecated use {@link #getAggregatableAttributionSourceV2(Trigger)} instead.
*/
+ @Deprecated
public Optional<AggregatableAttributionSource> getAggregatableAttributionSource()
throws JSONException {
if (mAggregatableAttributionSource == null) {
@@ -757,6 +761,30 @@
return mAggregatableAttributionSource;
}
+ /**
+ * Returns the AggregatableAttributionSource object, which is constructed using the aggregate
+ * source string and aggregate filter data string in Source.
+ */
+ public Optional<AggregatableAttributionSource> getAggregatableAttributionSourceV2(
+ @NonNull Trigger trigger) throws JSONException {
+ if (mAggregateSource == null) {
+ return Optional.empty();
+ }
+ JSONObject jsonObject = new JSONObject(mAggregateSource);
+ TreeMap<String, BigInteger> aggregateSourceMap = new TreeMap<>();
+ for (String key : jsonObject.keySet()) {
+ // Remove "0x" prefix.
+ String hexString = jsonObject.getString(key).substring(2);
+ BigInteger bigInteger = new BigInteger(hexString, 16);
+ aggregateSourceMap.put(key, bigInteger);
+ }
+ return Optional.of(
+ new AggregatableAttributionSource.Builder()
+ .setAggregatableSource(aggregateSourceMap)
+ .setFilterMap(getFilterData(trigger))
+ .build());
+ }
+
/** Returns the registration id. */
@Nullable
public String getRegistrationId() {
@@ -902,7 +930,10 @@
/**
* Generates AggregatableFilterData from aggregate filter string in Source, including an entry
* for source type.
+ *
+ * @deprecated use {@link #getFilterData(Trigger)} instead.
*/
+ @Deprecated
public FilterMap getFilterData() throws JSONException {
if (mFilterData != null) {
return mFilterData;
@@ -922,6 +953,21 @@
return mFilterData;
}
+ /**
+ * Generates AggregatableFilterData from aggregate filter string in Source, including entries
+ * for source type and duration from source to trigger.
+ */
+ public FilterMap getFilterData(@NonNull Trigger trigger) throws JSONException {
+ return new FilterMap.Builder()
+ .buildFilterDataV2(new JSONObject(mFilterDataString))
+ .addStringListValue(
+ "source_type", Collections.singletonList(mSourceType.getValue()))
+ .addLongValue(
+ FilterMap.LOOKBACK_WINDOW,
+ TimeUnit.MILLISECONDS.toSeconds(trigger.getTriggerTime() - mEventTime))
+ .build();
+ }
+
@Nullable
public UnsignedLong getSharedDebugKey() {
return mSharedDebugKey;
diff --git a/adservices/tests/unittest/service-core/src/com/android/adservices/service/measurement/FilterMapTest.java b/adservices/tests/unittest/service-core/src/com/android/adservices/service/measurement/FilterMapTest.java
index 8a9c347..0eb7452 100644
--- a/adservices/tests/unittest/service-core/src/com/android/adservices/service/measurement/FilterMapTest.java
+++ b/adservices/tests/unittest/service-core/src/com/android/adservices/service/measurement/FilterMapTest.java
@@ -16,6 +16,8 @@
package com.android.adservices.service.measurement;
+import static com.google.common.truth.Truth.assertThat;
+
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotEquals;
@@ -107,6 +109,23 @@
assertEquals(expected, actual);
}
+ @Test
+ public void testAddLongValue() {
+ assertThat(new FilterMap.Builder().addLongValue("a", 1L).build().getLongValue("a"))
+ .isEqualTo(1L);
+ }
+
+ @Test
+ public void testAddStringListValue() {
+ List<String> stringList = Arrays.asList("123", "456");
+ assertThat(
+ new FilterMap.Builder()
+ .addStringListValue("a", stringList)
+ .build()
+ .getStringListValue("a"))
+ .isEqualTo(stringList);
+ }
+
private FilterMap createExample() {
Map<String, List<String>> attributionFilterMap = new HashMap<>();
attributionFilterMap.put("type", Arrays.asList("1", "2", "3", "4"));
diff --git a/adservices/tests/unittest/service-core/src/com/android/adservices/service/measurement/SourceTest.java b/adservices/tests/unittest/service-core/src/com/android/adservices/service/measurement/SourceTest.java
index 3def290..4f922b3 100644
--- a/adservices/tests/unittest/service-core/src/com/android/adservices/service/measurement/SourceTest.java
+++ b/adservices/tests/unittest/service-core/src/com/android/adservices/service/measurement/SourceTest.java
@@ -16,6 +16,8 @@
package com.android.adservices.service.measurement;
+import static com.google.common.truth.Truth.assertThat;
+
import static org.junit.Assert.assertArrayEquals;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
@@ -780,6 +782,45 @@
}
@Test
+ public void testAggregatableAttributionSourceWithTrigger_addsLookbackWindow() throws Exception {
+ JSONObject aggregatableSource = new JSONObject();
+ aggregatableSource.put("campaignCounts", "0x159");
+ aggregatableSource.put("geoValue", "0x5");
+
+ JSONObject filterMapJson = new JSONObject();
+ filterMapJson.put("conversion", new JSONArray(Collections.singletonList("electronics")));
+
+ final Source source =
+ SourceFixture.getMinimalValidSourceBuilder()
+ .setAggregateSource(aggregatableSource.toString())
+ .setFilterData(filterMapJson.toString())
+ .build();
+
+ Trigger trigger = TriggerFixture.getValidTrigger();
+ Optional<AggregatableAttributionSource> aggregatableAttributionSource =
+ source.getAggregatableAttributionSourceV2(trigger);
+ assertThat(aggregatableAttributionSource.isPresent()).isTrue();
+ assertThat(aggregatableAttributionSource.get().getAggregatableSource())
+ .containsExactly(
+ "campaignCounts",
+ new BigInteger("159", 16),
+ "geoValue",
+ new BigInteger("5", 16));
+ assertThat(
+ aggregatableAttributionSource
+ .get()
+ .getFilterMap()
+ .getAttributionFilterMapWithLongValue())
+ .containsExactly(
+ "conversion",
+ FilterValue.ofStringList(Collections.singletonList("electronics")),
+ "source_type",
+ FilterValue.ofStringList(Collections.singletonList("event")),
+ FilterMap.LOOKBACK_WINDOW,
+ FilterValue.ofLong(8640000L));
+ }
+
+ @Test
public void testTriggerDataCardinality() {
Source eventSource =
SourceFixture.getMinimalValidSourceBuilder()
@@ -809,7 +850,7 @@
}
@Test
- public void testParseFilterData_nonEmpty() throws JSONException {
+ public void testGetFilterData_nonEmpty() throws JSONException {
JSONObject filterMapJson = new JSONObject();
filterMapJson.put("conversion", new JSONArray(Collections.singletonList("electronics")));
filterMapJson.put("product", new JSONArray(Arrays.asList("1234", "2345")));
@@ -824,12 +865,37 @@
filterMap.getAttributionFilterMap().get("conversion"));
assertEquals(Arrays.asList("1234", "2345"),
filterMap.getAttributionFilterMap().get("product"));
- assertEquals(Collections.singletonList("navigation"),
+ assertEquals(
+ Collections.singletonList("navigation"),
filterMap.getAttributionFilterMap().get("source_type"));
}
@Test
- public void testParseFilterData_nullFilterData() throws JSONException {
+ public void testGetFilterData_withTrigger_addsLookbackWindow() throws JSONException {
+ JSONObject filterMapJson = new JSONObject();
+ filterMapJson.put("conversion", new JSONArray(List.of("electronics")));
+ filterMapJson.put("product", new JSONArray(List.of("1234", "2345")));
+ Source source =
+ SourceFixture.getMinimalValidSourceBuilder()
+ .setSourceType(Source.SourceType.NAVIGATION)
+ .setFilterData(filterMapJson.toString())
+ .build();
+ Trigger trigger = TriggerFixture.getValidTrigger();
+ FilterMap filterMap = source.getFilterData(trigger);
+ assertThat(filterMap.getAttributionFilterMapWithLongValue())
+ .containsExactly(
+ "conversion",
+ FilterValue.ofStringList(List.of("electronics")),
+ "product",
+ FilterValue.ofStringList(List.of("1234", "2345")),
+ "source_type",
+ FilterValue.ofStringList(List.of("navigation")),
+ FilterMap.LOOKBACK_WINDOW,
+ FilterValue.ofLong(8640000L));
+ }
+
+ @Test
+ public void testGetFilterData_nullFilterData() throws JSONException {
Source source =
SourceFixture.getMinimalValidSourceBuilder()
.setSourceType(Source.SourceType.EVENT)
@@ -841,7 +907,7 @@
}
@Test
- public void testParseFilterData_emptyFilterData() throws JSONException {
+ public void testGetFilterData_emptyFilterData() throws JSONException {
Source source =
SourceFixture.getMinimalValidSourceBuilder()
.setSourceType(Source.SourceType.EVENT)