Add AggregatePayloadGenerator to generate the aggregate report.
Include filters.

Rename unencrypted report to CleartextAggregatePayload.
Rename encrypted report to AggregatePayload.

Test: atest
com.android.adservices.service.measurement.aggregation.AggregatePayloadGeneratorTest

Change-Id: Ibb9263f750bf3535fc3b216262fe2800248b0c1d
diff --git a/adservices/service-core/java/com/android/adservices/service/measurement/aggregation/AggregatableAttributionSource.java b/adservices/service-core/java/com/android/adservices/service/measurement/aggregation/AggregatableAttributionSource.java
index f4a6b9f..0e71637 100644
--- a/adservices/service-core/java/com/android/adservices/service/measurement/aggregation/AggregatableAttributionSource.java
+++ b/adservices/service-core/java/com/android/adservices/service/measurement/aggregation/AggregatableAttributionSource.java
@@ -26,9 +26,11 @@
 public class AggregatableAttributionSource {
 
     private Map<String, AttributionAggregatableKey> mAggregatableSource;
+    private AggregateFilterData mAggregateFilterData;
 
     private AggregatableAttributionSource() {
         mAggregatableSource = new HashMap<>();
+        mAggregateFilterData = null;
     }
 
     @Override
@@ -37,7 +39,8 @@
             return false;
         }
         AggregatableAttributionSource attributionSource = (AggregatableAttributionSource) obj;
-        return Objects.equals(mAggregatableSource, attributionSource.mAggregatableSource);
+        return Objects.equals(mAggregatableSource, attributionSource.mAggregatableSource)
+                && Objects.equals(mAggregateFilterData, attributionSource.mAggregateFilterData);
     }
 
     @Override
@@ -54,6 +57,13 @@
     }
 
     /**
+     * Returns aggregate filter data which represents a map in JSONObject.
+     */
+    public AggregateFilterData getAggregateFilterData() {
+        return mAggregateFilterData;
+    }
+
+    /**
      * Builder for {@link AggregatableAttributionSource}.
      */
     public static final class Builder {
@@ -73,6 +83,14 @@
         }
 
         /**
+         * See {@link AggregatableAttributionSource#getAggregateFilterData()}.
+         */
+        public Builder setAggregateFilterData(AggregateFilterData aggregateFilterData) {
+            mBuilding.mAggregateFilterData = aggregateFilterData;
+            return this;
+        }
+
+        /**
          * Build the  {@link AggregatableAttributionSource}.
          */
         public AggregatableAttributionSource build() {
diff --git a/adservices/service-core/java/com/android/adservices/service/measurement/aggregation/AggregatableAttributionTrigger.java b/adservices/service-core/java/com/android/adservices/service/measurement/aggregation/AggregatableAttributionTrigger.java
index 0fecbf7..dc673ef 100644
--- a/adservices/service-core/java/com/android/adservices/service/measurement/aggregation/AggregatableAttributionTrigger.java
+++ b/adservices/service-core/java/com/android/adservices/service/measurement/aggregation/AggregatableAttributionTrigger.java
@@ -28,7 +28,7 @@
 public class AggregatableAttributionTrigger {
 
     private List<AggregateTriggerData> mTriggerData;
-    private Map<String, Long> mValues;
+    private Map<String, Integer> mValues;
 
     private AggregatableAttributionTrigger() {
         mTriggerData = new ArrayList<>();
@@ -61,7 +61,7 @@
     /**
      * Returns the value map which contains the value for each aggregatable_source.
      */
-    public Map<String, Long> getValues() {
+    public Map<String, Integer> getValues() {
         return mValues;
     }
 
@@ -86,7 +86,7 @@
         /**
          * See {@link AggregatableAttributionTrigger#getValues()}.
          */
-        public Builder setValues(Map<String, Long> values) {
+        public Builder setValues(Map<String, Integer> values) {
             mBuilding.mValues = values;
             return this;
         }
diff --git a/adservices/service-core/java/com/android/adservices/service/measurement/aggregation/AggregateAttributionData.java b/adservices/service-core/java/com/android/adservices/service/measurement/aggregation/AggregateAttributionData.java
index 8a549e1..5ae679c 100644
--- a/adservices/service-core/java/com/android/adservices/service/measurement/aggregation/AggregateAttributionData.java
+++ b/adservices/service-core/java/com/android/adservices/service/measurement/aggregation/AggregateAttributionData.java
@@ -29,13 +29,10 @@
     private List<AggregateHistogramContribution> mContributions;
     @Nullable
     private Long mId;
-    @Nullable
-    private AggregateReport mAssembledReport;
 
     private AggregateAttributionData() {
         mContributions = new ArrayList<>();
         mId = null;
-        mAssembledReport = null;
     }
 
     @Override
@@ -45,13 +42,12 @@
         }
         AggregateAttributionData aggregateAttributionData = (AggregateAttributionData) obj;
         return Objects.equals(mContributions, aggregateAttributionData.mContributions)
-                && Objects.equals(mId, aggregateAttributionData.mId)
-                && Objects.equals(mAssembledReport, aggregateAttributionData.mAssembledReport);
+                && Objects.equals(mId, aggregateAttributionData.mId);
     }
 
     @Override
     public int hashCode() {
-        return Objects.hash(mContributions, mId, mAssembledReport);
+        return Objects.hash(mContributions, mId);
     }
 
     /**
@@ -71,14 +67,6 @@
     }
 
     /**
-     * The report assembled by the aggregation service. If null, the report has not been assembled.
-     */
-    @Nullable
-    public AggregateReport getAssembledReport() {
-        return mAssembledReport;
-    }
-
-    /**
      * Builder for {@link AggregateAttributionData}.
      */
     public static final class Builder {
@@ -105,14 +93,6 @@
         }
 
         /**
-         * See {@link AggregateAttributionData#getAssembledReport()}.
-         */
-        public Builder setAssembledReport(@Nullable AggregateReport aggregateReport) {
-            mAggregateAttributionData.mAssembledReport = aggregateReport;
-            return this;
-        }
-
-        /**
          * Build the {@link AggregateAttributionData}.
          */
         public AggregateAttributionData build() {
diff --git a/adservices/service-core/java/com/android/adservices/service/measurement/aggregation/AggregateReport.java b/adservices/service-core/java/com/android/adservices/service/measurement/aggregation/AggregatePayload.java
similarity index 72%
rename from adservices/service-core/java/com/android/adservices/service/measurement/aggregation/AggregateReport.java
rename to adservices/service-core/java/com/android/adservices/service/measurement/aggregation/AggregatePayload.java
index 3ca4162..d3d0a07 100644
--- a/adservices/service-core/java/com/android/adservices/service/measurement/aggregation/AggregateReport.java
+++ b/adservices/service-core/java/com/android/adservices/service/measurement/aggregation/AggregatePayload.java
@@ -21,25 +21,26 @@
 import java.util.Objects;
 
 /**
- * The aggregate report is the attribution report encrypted by the aggregation service. An aggregate
- * report contains all the information needed for sending the report to its reporting endpoint. All
- * nested information has already been serialized and encrypted as necessary.
+ * The aggregate payload is the aggregate report encrypted by the aggregation service.
+ * An aggregate report contains all the information needed for sending the report to its
+ * reporting endpoint. All nested information has already been serialized and encrypted as
+ * necessary.
  */
-public class AggregateReport {
+public class AggregatePayload {
     private List<AggregationServicePayload> mPayloads;
     private String mSharedInfo;
 
-    private AggregateReport() {
+    private AggregatePayload() {
         mPayloads = new ArrayList<>();
         mSharedInfo = null;
     }
 
     @Override
     public boolean equals(Object obj) {
-        if (!(obj instanceof AggregateReport)) {
+        if (!(obj instanceof AggregatePayload)) {
             return false;
         }
-        AggregateReport aggregateReport = (AggregateReport) obj;
+        AggregatePayload aggregateReport = (AggregatePayload) obj;
         return mPayloads.equals(aggregateReport.mPayloads)
                 && Objects.equals(mSharedInfo, aggregateReport.mSharedInfo);
     }
@@ -50,7 +51,7 @@
     }
 
     /**
-     * All AggregationServicePayload generated in the aggregate report.
+     * All AggregationServicePayload generated in the encrypted aggregate report.
      */
     public List<AggregationServicePayload> getPayloads() {
         return mPayloads;
@@ -64,17 +65,17 @@
     }
 
     /**
-     * Builder for {@link AggregateReport}
+     * Builder for {@link AggregatePayload}
      */
     public static final class Builder {
-        private final AggregateReport mAggregateReport;
+        private final AggregatePayload mAggregateReport;
 
         public Builder() {
-            mAggregateReport = new AggregateReport();
+            mAggregateReport = new AggregatePayload();
         }
 
         /**
-         * See {@link AggregateReport#getPayloads()} ()}.
+         * See {@link AggregatePayload#getPayloads()} ()}.
          */
         public Builder setAggregationServicePayload(
                 List<AggregationServicePayload> payloads) {
@@ -83,7 +84,7 @@
         }
 
         /**
-         * See {@link AggregateReport#getSharedInfo()} ()}.
+         * See {@link AggregatePayload#getSharedInfo()} ()}.
          */
         public Builder setSharedInfo(String sharedInfo) {
             mAggregateReport.mSharedInfo = sharedInfo;
@@ -91,28 +92,24 @@
         }
 
         /**
-         * Build the {@link AggregateReport}.
+         * Build the {@link AggregatePayload}.
          */
-        public AggregateReport build() {
+        public AggregatePayload build() {
             return mAggregateReport;
         }
     }
 
     /**
-     * This payload is constructed using the data in the AttributionReport and then encrypted with
-     * one of `url`'s public keys. The plaintext of the encrypted payload is a serialized CBOR map
-     * structured as follows:
-     *  {
-     *  "operation": "<chosen operation as string>",
-     *  "data": [{
-     *      "bucket": <a 16-byte (i.e. 128-bit) big-endian bytestring>,
-     *      "value": <a 4-byte (i.e. 32-bit) big-endian bytestring>
-     *  }, ...],
-     *  }
-     *  Note that the "data" array may contain multiple contributions.
-     *  For the `kExperimentalPoplar` aggregation mode, the "data" field is
-     *  replaced with:
-     *  "dpf_key": <binary serialization of the DPF key>
+     * Support a list of payloads for future extensibility if multiple helpers
+     * are necessary. Currently only supports a single helper configured
+     * by the browser.
+     *   "aggregation_service_payloads": [
+     *     {
+     *       "payload": "[base64-encoded HPKE encrypted data readable only by the aggregation
+     *       service]",
+     *       "key_id": "[string identifying public key used to encrypt payload]",
+     *     },
+     *   ],
      */
     public static class AggregationServicePayload {
         private List<Integer> mPayload;
diff --git a/adservices/service-core/java/com/android/adservices/service/measurement/aggregation/AggregatePayloadGenerator.java b/adservices/service-core/java/com/android/adservices/service/measurement/aggregation/AggregatePayloadGenerator.java
new file mode 100644
index 0000000..8383238
--- /dev/null
+++ b/adservices/service-core/java/com/android/adservices/service/measurement/aggregation/AggregatePayloadGenerator.java
@@ -0,0 +1,138 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.adservices.service.measurement.aggregation;
+
+import java.math.BigInteger;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Optional;
+import java.util.Set;
+
+/**
+ * Class used to generate CleartextAggregatePayload using AggregatableAttributionSource and
+ * AggregatableAttributionTrigger.
+ */
+public class AggregatePayloadGenerator {
+
+    private AggregatePayloadGenerator() {}
+
+    /**
+     * Generates the {@link CleartextAggregatePayload} from given AggregatableAttributionSource and
+     * AggregatableAttributionTrigger.
+     *
+     * @param attributionSource the aggregate attribution source used for aggregation.
+     * @param attributionTrigger the aggregate attribution trigger used for aggregation.
+     * @return the aggregate report generated by the given aggregate attribution source and
+     * aggregate attribution trigger.
+     */
+    public static Optional<CleartextAggregatePayload> generateAttributionReport(
+            AggregatableAttributionSource attributionSource,
+            AggregatableAttributionTrigger attributionTrigger) {
+        AggregateFilterData sourceFilterData = attributionSource.getAggregateFilterData();
+        Map<String, BigInteger> aggregateKeys = new HashMap<>();
+        Map<String, AttributionAggregatableKey> aggregateSourceMap =
+                attributionSource.getAggregatableSource();
+        for (String sourceKey : aggregateSourceMap.keySet()) {
+            for (AggregateTriggerData triggerData : attributionTrigger.getTriggerData()) {
+                Optional<AggregateFilterData> filterData = triggerData.getFilter();
+                Optional<AggregateFilterData> notFilterData = triggerData.getNotFilter();
+                // Skip this trigger data when filter doesn't match.
+                if (filterData.isPresent()
+                        && !isFilterMatch(sourceFilterData, filterData.get(), true)) {
+                    continue;
+                }
+                // Skip this trigger data when not_filters doesn't match.
+                if (notFilterData.isPresent()
+                        && !isFilterMatch(sourceFilterData, notFilterData.get(), false)) {
+                    continue;
+                }
+                if (triggerData.getSourceKeys().contains(sourceKey)) {
+                    AttributionAggregatableKey currentKey = aggregateSourceMap.get(sourceKey);
+                    AttributionAggregatableKey triggerKey = triggerData.getKey();
+                    BigInteger currentInt;
+                    if (aggregateKeys.containsKey(sourceKey)) {
+                        currentInt = aggregateKeys.get(sourceKey);
+                    } else {
+                        currentInt = BigInteger.valueOf(
+                                (long) (Math.pow(2, 63) * currentKey.getHighBits()
+                                        + currentKey.getLowBits()));
+                    }
+                    BigInteger triggerInt = BigInteger.valueOf(
+                            (long) (Math.pow(2, 63) * triggerKey.getHighBits()
+                                    + triggerKey.getLowBits()));
+                    aggregateKeys.put(sourceKey, currentInt.add(triggerInt));
+                }
+            }
+        }
+
+        List<AggregateHistogramContribution> contributions = new ArrayList<>();
+        for (String key : attributionTrigger.getValues().keySet()) {
+            if (aggregateKeys.containsKey(key)) {
+                AggregateHistogramContribution contribution =
+                        new AggregateHistogramContribution.Builder()
+                                .setKey(aggregateKeys.get(key))
+                                .setValue(attributionTrigger.getValues().get(key)).build();
+                contributions.add(contribution);
+            }
+        }
+        if (contributions.size() > 0) {
+            return Optional.of(new CleartextAggregatePayload.Builder()
+                    .setAggregateAttributionData(
+                            new AggregateAttributionData.Builder()
+                                    .setContributions(contributions).build()).build());
+        }
+        return Optional.empty();
+    }
+
+    /**
+     * Checks whether source filter and trigger filter are matched.
+     * When a key is only present in source or trigger, ignore that key.
+     * When a key is present both in source and trigger, the key matches if the intersection of
+     * values is not empty.
+     * @param sourceFilter the filter_data field in attribution source.
+     * @param triggerFilter the AttributionTriggerData in attribution trigger.
+     * @param isFilter true for filters, false for not_filters.
+     * @return return true when all keys in source filter and trigger filter are matched.
+     */
+    public static boolean isFilterMatch(AggregateFilterData sourceFilter,
+            AggregateFilterData triggerFilter, boolean isFilter) {
+        for (String key : triggerFilter.getAttributionFilterMap().keySet()) {
+            if (!sourceFilter.getAttributionFilterMap().containsKey(key)) {
+                continue;
+            }
+            // Finds the intersection of two value lists.
+            List<String> sourceValues = sourceFilter.getAttributionFilterMap().get(key);
+            List<String> triggerValues = triggerFilter.getAttributionFilterMap().get(key);
+            Set<String> common = new HashSet<>(sourceValues);
+            common.retainAll(triggerValues);
+            // For filters, return false when one key doesn't have intersection.
+            if (isFilter && common.size() == 0) {
+                return false;
+            }
+            // For not_filters, return false when one key has intersection.
+            if (!isFilter && common.size() != 0) {
+                return false;
+            }
+        }
+        return true;
+    }
+}
+
+
diff --git a/adservices/service-core/java/com/android/adservices/service/measurement/aggregation/AggregateTriggerData.java b/adservices/service-core/java/com/android/adservices/service/measurement/aggregation/AggregateTriggerData.java
index 0dc2ba7..3b2d0ba 100644
--- a/adservices/service-core/java/com/android/adservices/service/measurement/aggregation/AggregateTriggerData.java
+++ b/adservices/service-core/java/com/android/adservices/service/measurement/aggregation/AggregateTriggerData.java
@@ -18,6 +18,7 @@
 
 import java.util.HashSet;
 import java.util.Objects;
+import java.util.Optional;
 import java.util.Set;
 
 /**
@@ -27,10 +28,14 @@
 
     private AttributionAggregatableKey mKey;
     private Set<String> mSourceKeys;
+    private Optional<AggregateFilterData> mFilter;
+    private Optional<AggregateFilterData> mNotFilter;
 
     private AggregateTriggerData() {
         mKey = null;
         mSourceKeys = new HashSet<>();
+        mFilter = Optional.empty();
+        mNotFilter = Optional.empty();
     }
 
     @Override
@@ -40,7 +45,9 @@
         }
         AggregateTriggerData attributionTriggerData = (AggregateTriggerData) obj;
         return Objects.equals(mKey, attributionTriggerData.mKey)
-                && Objects.equals(mSourceKeys, attributionTriggerData.mSourceKeys);
+                && Objects.equals(mSourceKeys, attributionTriggerData.mSourceKeys)
+                && Objects.equals(mFilter, attributionTriggerData.mFilter)
+                && Objects.equals(mNotFilter, attributionTriggerData.mNotFilter);
     }
 
     @Override
@@ -63,6 +70,21 @@
     }
 
     /**
+     * Returns the filter which controls when aggregate trigger data ise used based on impression
+     * side information.
+     */
+    public Optional<AggregateFilterData> getFilter() {
+        return mFilter;
+    }
+
+    /**
+     * Returns the not_filter, reverse of filter.
+     */
+    public Optional<AggregateFilterData> getNotFilter() {
+        return mNotFilter;
+    }
+
+    /**
      * Builder for {@link AggregateTriggerData}.
      */
     public static final class Builder {
@@ -89,6 +111,22 @@
         }
 
         /**
+         * See {@link AggregateTriggerData#getFilter()}.
+         */
+        public Builder setFilter(AggregateFilterData filter) {
+            mBuilding.mFilter = Optional.of(filter);
+            return this;
+        }
+
+        /**
+         * See {@link AggregateTriggerData#getNotFilter()}
+         */
+        public Builder setNotFilter(AggregateFilterData notFilter) {
+            mBuilding.mNotFilter = Optional.of(notFilter);
+            return this;
+        }
+
+        /**
          * Build the {@link AggregateTriggerData}
          */
         public AggregateTriggerData build() {
diff --git a/adservices/service-core/java/com/android/adservices/service/measurement/aggregation/AttributionReport.java b/adservices/service-core/java/com/android/adservices/service/measurement/aggregation/CleartextAggregatePayload.java
similarity index 70%
rename from adservices/service-core/java/com/android/adservices/service/measurement/aggregation/AttributionReport.java
rename to adservices/service-core/java/com/android/adservices/service/measurement/aggregation/CleartextAggregatePayload.java
index da37722..364d19f 100644
--- a/adservices/service-core/java/com/android/adservices/service/measurement/aggregation/AttributionReport.java
+++ b/adservices/service-core/java/com/android/adservices/service/measurement/aggregation/CleartextAggregatePayload.java
@@ -16,21 +16,33 @@
 
 package com.android.adservices.service.measurement.aggregation;
 
-import android.annotation.Nullable;
+import android.annotation.IntDef;
 
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
 import java.util.Objects;
 
 /**
- * Class that contains all the data needed to serialize and send an attribution report. This class
- * can represent multiple different types of reports.
+ * Class that contains all the real data needed after aggregation, it is not encrypted.
  */
-public class AttributionReport {
+public class CleartextAggregatePayload {
     private AttributionInfo mAttributionInfo;
     private long mReportTime;
     private long mExternalReportId;
     private AggregateAttributionData mAggregateAttributionData;
+    private @Status int mStatus;
 
-    private AttributionReport() {
+    @IntDef(value = {
+            Status.PENDING,
+            Status.DELIVERED,
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface Status {
+        int PENDING = 0;
+        int DELIVERED = 1;
+    }
+
+    private CleartextAggregatePayload() {
         mAttributionInfo = null;
         mReportTime = 0L;
         mExternalReportId = 0L;
@@ -39,11 +51,12 @@
 
     @Override
     public boolean equals(Object obj) {
-        if (!(obj instanceof AttributionReport)) {
+        if (!(obj instanceof CleartextAggregatePayload)) {
             return false;
         }
-        AttributionReport attributionReport = (AttributionReport) obj;
-        return Objects.equals(mAttributionInfo, attributionReport.mAttributionInfo)
+        CleartextAggregatePayload attributionReport = (CleartextAggregatePayload) obj;
+        return mStatus == attributionReport.mStatus
+                && Objects.equals(mAttributionInfo, attributionReport.mAttributionInfo)
                 && Objects.equals(mReportTime, attributionReport.mReportTime)
                 && Objects.equals(mExternalReportId, attributionReport.mExternalReportId)
                 && Objects.equals(mAggregateAttributionData,
@@ -52,7 +65,7 @@
 
     @Override
     public int hashCode() {
-        return Objects.hash(mAttributionInfo, mReportTime, mExternalReportId,
+        return Objects.hash(mStatus, mAttributionInfo, mReportTime, mExternalReportId,
                 mAggregateAttributionData);
     }
 
@@ -85,17 +98,24 @@
     }
 
     /**
-     * Builder for {@link AttributionReport}.
+     * Current {@link Status} of the report.
+     */
+    public @Status int getStatus() {
+        return mStatus;
+    }
+
+    /**
+     * Builder for {@link CleartextAggregatePayload}.
      */
     public static final class Builder {
-        private final AttributionReport mAttributionReport;
+        private final CleartextAggregatePayload mAttributionReport;
 
         public Builder() {
-            mAttributionReport = new AttributionReport();
+            mAttributionReport = new CleartextAggregatePayload();
         }
 
         /**
-         * See {@link AttributionReport#getAttributionInfo()}.
+         * See {@link CleartextAggregatePayload#getAttributionInfo()}.
          */
         public Builder setAttributionInfo(AttributionInfo attributionInfo) {
             mAttributionReport.mAttributionInfo = attributionInfo;
@@ -103,7 +123,7 @@
         }
 
         /**
-         * See {@link AttributionReport#getReportTime()}.
+         * See {@link CleartextAggregatePayload#getReportTime()}.
          */
         public Builder setReportTime(long reportTime) {
             mAttributionReport.mReportTime = reportTime;
@@ -111,7 +131,7 @@
         }
 
         /**
-         * See {@link AttributionReport#getExternalReportId()}.
+         * See {@link CleartextAggregatePayload#getExternalReportId()}.
          */
         public Builder setExternalReportId(long externalReportId) {
             mAttributionReport.mExternalReportId = externalReportId;
@@ -119,7 +139,7 @@
         }
 
         /**
-         * See {@link AttributionReport#getAggregateAttributionData()}.
+         * See {@link CleartextAggregatePayload#getAggregateAttributionData()}.
          */
         public Builder setAggregateAttributionData(
                 AggregateAttributionData aggregateAttributionData) {
@@ -128,9 +148,17 @@
         }
 
         /**
-         * Build the {@link AttributionReport}.
+         * See {@link CleartextAggregatePayload#getStatus()}
          */
-        public AttributionReport build() {
+        public Builder setStatus(@Status int status) {
+            mAttributionReport.mStatus = status;
+            return this;
+        }
+
+        /**
+         * Build the {@link CleartextAggregatePayload}.
+         */
+        public CleartextAggregatePayload build() {
             return mAttributionReport;
         }
     }
@@ -142,12 +170,9 @@
         // TODO: Add StoredSource object here later.
 
         private long mTime;
-        @Nullable
-        private Long mDebugkey;
 
         private AttributionInfo() {
             mTime = 0L;
-            mDebugkey = 0L;
         }
 
         @Override
@@ -156,13 +181,12 @@
                 return false;
             }
             AttributionInfo attributionInfo = (AttributionInfo) obj;
-            return Objects.equals(mTime, attributionInfo.mTime)
-                    && Objects.equals(mDebugkey, attributionInfo.mDebugkey);
+            return Objects.equals(mTime, attributionInfo.mTime);
         }
 
         @Override
         public int hashCode() {
-            return Objects.hash(mTime, mDebugkey);
+            return Objects.hash(mTime);
         }
 
         /**
@@ -173,14 +197,6 @@
         }
 
         /**
-         * Debug key for the attribution info.
-         */
-        @Nullable
-        public Long getDebugkey() {
-            return mDebugkey;
-        }
-
-        /**
          * Builder for {@link AttributionInfo}.
          */
         public static final class Builder {
@@ -199,14 +215,6 @@
             }
 
             /**
-             * See {@link AttributionInfo#getDebugkey()}.
-             */
-            public Builder setDebugkey(@Nullable Long debugkey) {
-                mAttributionInfo.mDebugkey = debugkey;
-                return this;
-            }
-
-            /**
              * Build the {@link AttributionInfo}.
              */
             public AttributionInfo build() {
diff --git a/adservices/tests/unittest/service-core/src/com/android/adservices/service/measurement/aggregation/AggregatableAttributionSourceTest.java b/adservices/tests/unittest/service-core/src/com/android/adservices/service/measurement/aggregation/AggregatableAttributionSourceTest.java
index 01bab64..f2d1efb 100644
--- a/adservices/tests/unittest/service-core/src/com/android/adservices/service/measurement/aggregation/AggregatableAttributionSourceTest.java
+++ b/adservices/tests/unittest/service-core/src/com/android/adservices/service/measurement/aggregation/AggregatableAttributionSourceTest.java
@@ -17,12 +17,15 @@
 package com.android.adservices.service.measurement.aggregation;
 
 import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNull;
 
 import androidx.test.filters.SmallTest;
 
 import org.junit.Test;
 
+import java.util.Arrays;
 import java.util.HashMap;
+import java.util.List;
 import java.util.Map;
 
 /** Unit tests for {@link AggregatableAttributionSource} */
@@ -36,10 +39,17 @@
                 new AttributionAggregatableKey.Builder().setHighBits(0L).setLowBits(159L).build());
         aggregatableSource.put("geoValue",
                 new AttributionAggregatableKey.Builder().setHighBits(0L).setLowBits(5L).build());
+        Map<String, List<String>> aggregateFilterData = new HashMap<>();
+        aggregateFilterData.put("conversion_subdomain", Arrays.asList("electronics.megastore"));
+        aggregateFilterData.put("product", Arrays.asList("1234", "2345"));
 
         AggregatableAttributionSource attributionSource =
                 new AggregatableAttributionSource.Builder()
-                        .setAggregatableSource(aggregatableSource).build();
+                        .setAggregatableSource(aggregatableSource)
+                        .setAggregateFilterData(
+                                new AggregateFilterData.Builder()
+                                        .setAttributionFilterMap(aggregateFilterData).build())
+                        .build();
 
         assertEquals(attributionSource.getAggregatableSource().size(), 2);
         assertEquals(attributionSource.getAggregatableSource().get("campaignCounts")
@@ -50,6 +60,10 @@
                 .getHighBits().longValue(), 0L);
         assertEquals(attributionSource.getAggregatableSource().get("geoValue")
                 .getLowBits().longValue(), 5L);
+        assertEquals(attributionSource.getAggregateFilterData().getAttributionFilterMap()
+                .get("conversion_subdomain").size(), 1);
+        assertEquals(attributionSource.getAggregateFilterData().getAttributionFilterMap()
+                .get("product").size(), 2);
     }
 
     @Test
@@ -57,5 +71,6 @@
         AggregatableAttributionSource attributionSource =
                 new AggregatableAttributionSource.Builder().build();
         assertEquals(attributionSource.getAggregatableSource().size(), 0);
+        assertNull(attributionSource.getAggregateFilterData());
     }
 }
diff --git a/adservices/tests/unittest/service-core/src/com/android/adservices/service/measurement/aggregation/AggregatableAttributionTriggerTest.java b/adservices/tests/unittest/service-core/src/com/android/adservices/service/measurement/aggregation/AggregatableAttributionTriggerTest.java
index b82f3ec..39aea00 100644
--- a/adservices/tests/unittest/service-core/src/com/android/adservices/service/measurement/aggregation/AggregatableAttributionTriggerTest.java
+++ b/adservices/tests/unittest/service-core/src/com/android/adservices/service/measurement/aggregation/AggregatableAttributionTriggerTest.java
@@ -48,9 +48,9 @@
                                 Arrays.asList("campCounts", "campGeoCounts", "campGeoValue")))
                         .build();
 
-        Map<String, Long> values = new HashMap<>();
-        values.put("campCounts", 1L);
-        values.put("campGeoCounts", 100L);
+        Map<String, Integer> values = new HashMap<>();
+        values.put("campCounts", 1);
+        values.put("campGeoCounts", 100);
 
         AggregatableAttributionTrigger attributionTrigger =
                 new AggregatableAttributionTrigger.Builder()
@@ -69,8 +69,8 @@
         assertEquals(attributionTrigger.getTriggerData().get(1).getKey().getLowBits().longValue(),
                 5L);
         assertEquals(attributionTrigger.getTriggerData().get(1).getSourceKeys().size(), 3);
-        assertEquals(attributionTrigger.getValues().get("campCounts").longValue(), 1L);
-        assertEquals(attributionTrigger.getValues().get("campGeoCounts").longValue(), 100L);
+        assertEquals(attributionTrigger.getValues().get("campCounts").intValue(), 1);
+        assertEquals(attributionTrigger.getValues().get("campGeoCounts").intValue(), 100);
     }
 
     @Test
diff --git a/adservices/tests/unittest/service-core/src/com/android/adservices/service/measurement/aggregation/AggregateAttributionDataTest.java b/adservices/tests/unittest/service-core/src/com/android/adservices/service/measurement/aggregation/AggregateAttributionDataTest.java
index 42751ef..de75336 100644
--- a/adservices/tests/unittest/service-core/src/com/android/adservices/service/measurement/aggregation/AggregateAttributionDataTest.java
+++ b/adservices/tests/unittest/service-core/src/com/android/adservices/service/measurement/aggregation/AggregateAttributionDataTest.java
@@ -33,8 +33,7 @@
     private AggregateAttributionData createExample() {
         return new AggregateAttributionData.Builder()
                 .setContributions(new ArrayList<>())
-                .setId(1L)
-                .setAssembledReport(new AggregateReport.Builder().build()).build();
+                .setId(1L).build();
     }
 
     @Test
@@ -42,7 +41,6 @@
         AggregateAttributionData data = createExample();
         assertNotNull(data.getContributions());
         assertEquals(1L, data.getId().longValue());
-        assertNotNull(data.getAssembledReport());
     }
 
     @Test
@@ -50,6 +48,6 @@
         AggregateAttributionData data = new AggregateAttributionData.Builder().build();
         assertEquals(0, data.getContributions().size());
         assertNull(data.getId());
-        assertNull(data.getAssembledReport());
     }
 }
+
diff --git a/adservices/tests/unittest/service-core/src/com/android/adservices/service/measurement/aggregation/AggregatePayloadGeneratorTest.java b/adservices/tests/unittest/service-core/src/com/android/adservices/service/measurement/aggregation/AggregatePayloadGeneratorTest.java
new file mode 100644
index 0000000..0d851b8
--- /dev/null
+++ b/adservices/tests/unittest/service-core/src/com/android/adservices/service/measurement/aggregation/AggregatePayloadGeneratorTest.java
@@ -0,0 +1,354 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.adservices.service.measurement.aggregation;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import androidx.test.filters.SmallTest;
+
+import org.junit.Test;
+
+import java.math.BigInteger;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Optional;
+
+/** Unit tests for {@link AggregatePayloadGenerator} */
+@SmallTest
+public final class AggregatePayloadGeneratorTest {
+
+    @Test
+    public void testIsFilterMatchReturnTrue() {
+        Map<String, List<String>> sourceFilterMap = new HashMap<>();
+        sourceFilterMap.put("conversion_subdomain",
+                Collections.singletonList("electronics.megastore"));
+        sourceFilterMap.put("product", Arrays.asList("1234", "234"));
+        sourceFilterMap.put("ctid", Collections.singletonList("id"));
+        AggregateFilterData sourceFilter =  new AggregateFilterData.Builder()
+                .setAttributionFilterMap(sourceFilterMap).build();
+
+        Map<String, List<String>> triggerFilterMap = new HashMap<>();
+        triggerFilterMap.put("conversion_subdomain",
+                Collections.singletonList("electronics.megastore"));
+        triggerFilterMap.put("product", Arrays.asList("1234", "2345"));
+        triggerFilterMap.put("id", Arrays.asList("1", "2"));
+        AggregateFilterData triggerFilter =  new AggregateFilterData.Builder()
+                .setAttributionFilterMap(triggerFilterMap).build();
+
+        assertTrue(
+                AggregatePayloadGenerator.isFilterMatch(sourceFilter, triggerFilter, true));
+    }
+
+    @Test
+    public void testIsFilterMatchReturnFalse() {
+        Map<String, List<String>> sourceFilterMap = new HashMap<>();
+        sourceFilterMap.put("conversion_subdomain",
+                Collections.singletonList("electronics.megastore"));
+        sourceFilterMap.put("product", Arrays.asList("1234", "234"));
+        sourceFilterMap.put("ctid", Collections.singletonList("id"));
+        AggregateFilterData sourceFilter =  new AggregateFilterData.Builder()
+                .setAttributionFilterMap(sourceFilterMap).build();
+
+        Map<String, List<String>> triggerFilterMap = new HashMap<>();
+        triggerFilterMap.put("conversion_subdomain",
+                Collections.singletonList("electronics.megastore"));
+        triggerFilterMap.put("product", Arrays.asList("1", "2"));  // doesn't match.
+        triggerFilterMap.put("id", Arrays.asList("1", "2"));
+        AggregateFilterData triggerFilter =  new AggregateFilterData.Builder()
+                .setAttributionFilterMap(triggerFilterMap).build();
+
+        assertFalse(
+                AggregatePayloadGenerator.isFilterMatch(sourceFilter, triggerFilter, true));
+    }
+
+    @Test
+    public void testIsNotFilterMatchReturnTrue() {
+        Map<String, List<String>> sourceFilterMap = new HashMap<>();
+        sourceFilterMap.put("conversion_subdomain",
+                Collections.singletonList("electronics.megastore"));
+        sourceFilterMap.put("product", Arrays.asList("1234", "234"));
+        sourceFilterMap.put("ctid", Collections.singletonList("id"));
+        AggregateFilterData sourceFilter =  new AggregateFilterData.Builder()
+                .setAttributionFilterMap(sourceFilterMap).build();
+
+        Map<String, List<String>> triggerFilterMap = new HashMap<>();
+        triggerFilterMap.put("conversion_subdomain", Collections.singletonList("electronics"));
+        triggerFilterMap.put("product", Arrays.asList("1", "2"));  // doesn't match.
+        triggerFilterMap.put("id", Arrays.asList("1", "2"));
+        AggregateFilterData triggerFilter =  new AggregateFilterData.Builder()
+                .setAttributionFilterMap(triggerFilterMap).build();
+        assertTrue(AggregatePayloadGenerator.isFilterMatch(
+                sourceFilter, triggerFilter, false));
+    }
+
+    @Test
+    public void testIsNotFilterMatchReturnFalse() {
+        Map<String, List<String>> sourceFilterMap = new HashMap<>();
+        sourceFilterMap.put("conversion_subdomain",
+                Collections.singletonList("electronics.megastore"));
+        sourceFilterMap.put("product", Arrays.asList("1234", "234"));
+        sourceFilterMap.put("ctid", Collections.singletonList("id"));
+        AggregateFilterData sourceFilter =  new AggregateFilterData.Builder()
+                .setAttributionFilterMap(sourceFilterMap).build();
+
+        Map<String, List<String>> triggerFilterMap = new HashMap<>();
+        triggerFilterMap.put("conversion_subdomain",
+                Collections.singletonList("electronics.megastore"));
+        triggerFilterMap.put("product", Arrays.asList("1234", "2345"));
+        triggerFilterMap.put("id", Arrays.asList("1", "2"));
+        AggregateFilterData triggerFilter =  new AggregateFilterData.Builder()
+                .setAttributionFilterMap(triggerFilterMap).build();
+
+        assertFalse(
+                AggregatePayloadGenerator.isFilterMatch(sourceFilter, triggerFilter, false));
+    }
+
+    @Test
+    public void testGenerateAttributionReportTwoContributionsSuccessfully() {
+        // Build AggregatableAttributionSource.
+        Map<String, AttributionAggregatableKey> aggregatableSource = new HashMap<>();
+        aggregatableSource.put("campaignCounts",
+                new AttributionAggregatableKey.Builder().setHighBits(0L).setLowBits(345L).build());
+        aggregatableSource.put("geoValue",
+                new AttributionAggregatableKey.Builder().setHighBits(0L).setLowBits(5L).build());
+        Map<String, List<String>> sourceFilterMap = new HashMap<>();
+        sourceFilterMap.put("conversion_subdomain",
+                Collections.singletonList("electronics.megastore"));
+        sourceFilterMap.put("product", Arrays.asList("1234", "234"));
+        sourceFilterMap.put("ctid", Collections.singletonList("id"));
+        AggregateFilterData sourceFilter =  new AggregateFilterData.Builder()
+                .setAttributionFilterMap(sourceFilterMap).build();
+        AggregatableAttributionSource attributionSource =
+                new AggregatableAttributionSource.Builder()
+                        .setAggregatableSource(aggregatableSource)
+                        .setAggregateFilterData(sourceFilter).build();
+
+        // Build AggregatableAttributionTrigger.
+        List<AggregateTriggerData> triggerDataList = new ArrayList<>();
+        // Apply this key_piece to "campaignCounts".
+        Map<String, List<String>> triggerDataFilter1 = new HashMap<>();
+        triggerDataFilter1.put("product", Collections.singletonList("1234"));
+        triggerDataFilter1.put("ctid", Collections.singletonList("id"));
+        Map<String, List<String>> triggerDataNotFilter1 = new HashMap<>();
+        triggerDataNotFilter1.put("product", Collections.singletonList("100"));
+        triggerDataList.add(
+                new AggregateTriggerData.Builder()
+                        .setKey(new AttributionAggregatableKey.Builder()
+                                .setHighBits(0L).setLowBits(1024L).build())
+                        .setSourceKeys(new HashSet<>(Collections.singletonList("campaignCounts")))
+                        .setFilter(new AggregateFilterData.Builder()
+                                .setAttributionFilterMap(triggerDataFilter1).build())
+                        .setNotFilter(new AggregateFilterData.Builder()
+                                .setAttributionFilterMap(triggerDataNotFilter1).build()).build());
+        // Apply this key_piece to "geoValue".
+        triggerDataList.add(
+                new AggregateTriggerData.Builder()
+                        .setKey(new AttributionAggregatableKey.Builder()
+                                .setHighBits(0L).setLowBits(2688L).build())
+                        .setSourceKeys(new HashSet<>(Arrays.asList("geoValue", "nonMatch")))
+                        .build());
+
+        Map<String, Integer> values = new HashMap<>();
+        values.put("campaignCounts", 32768);
+        values.put("geoValue", 1664);
+        AggregatableAttributionTrigger attributionTrigger =
+                new AggregatableAttributionTrigger.Builder()
+                        .setTriggerData(triggerDataList)
+                        .setValues(values).build();
+
+        Optional<CleartextAggregatePayload> attributionReport =
+                AggregatePayloadGenerator.generateAttributionReport(
+                        attributionSource, attributionTrigger);
+        assertTrue(attributionReport.isPresent());
+        List<AggregateHistogramContribution> contributions =
+                attributionReport.get().getAggregateAttributionData().getContributions();
+
+        assertEquals(contributions.size(), 2);
+        assertTrue(contributions.contains(
+                new AggregateHistogramContribution.Builder()
+                        .setKey(BigInteger.valueOf(1369L)).setValue(32768).build()));
+        assertTrue(contributions.contains(
+                new AggregateHistogramContribution.Builder()
+                        .setKey(BigInteger.valueOf(2693L)).setValue(1664).build()));
+    }
+
+    @Test
+    public void testGenerateAttributionReportOnlyTwoContributionsSuccessfully() {
+        // Build AggregatableAttributionSource.
+        Map<String, AttributionAggregatableKey> aggregatableSource = new HashMap<>();
+        aggregatableSource.put("campaignCounts",
+                new AttributionAggregatableKey.Builder().setHighBits(0L).setLowBits(345L).build());
+        aggregatableSource.put("geoValue",
+                new AttributionAggregatableKey.Builder().setHighBits(0L).setLowBits(5L).build());
+        aggregatableSource.put("thirdSource",
+                new AttributionAggregatableKey.Builder().setHighBits(0L).setLowBits(100L).build());
+        Map<String, List<String>> sourceFilterMap = new HashMap<>();
+        sourceFilterMap.put("conversion_subdomain",
+                Collections.singletonList("electronics.megastore"));
+        sourceFilterMap.put("product", Arrays.asList("1234", "234"));
+        sourceFilterMap.put("ctid", Collections.singletonList("id"));
+        AggregateFilterData sourceFilter =  new AggregateFilterData.Builder()
+                .setAttributionFilterMap(sourceFilterMap).build();
+        AggregatableAttributionSource attributionSource =
+                new AggregatableAttributionSource.Builder()
+                        .setAggregatableSource(aggregatableSource)
+                        .setAggregateFilterData(sourceFilter).build();
+        // Build AggregatableAttributionTrigger.
+        List<AggregateTriggerData> triggerDataList = new ArrayList<>();
+        // Apply this key_piece to "campaignCounts".
+        Map<String, List<String>> triggerDataFilter1 = new HashMap<>();
+        triggerDataFilter1.put("product", Collections.singletonList("1234"));
+        triggerDataFilter1.put("ctid", Collections.singletonList("id"));
+        Map<String, List<String>> triggerDataNotFilter1 = new HashMap<>();
+        triggerDataNotFilter1.put("product", Collections.singletonList("100"));
+        triggerDataList.add(
+                new AggregateTriggerData.Builder()
+                        .setKey(new AttributionAggregatableKey.Builder()
+                                .setHighBits(0L).setLowBits(1024L).build())
+                        .setSourceKeys(new HashSet<>(Collections.singletonList("campaignCounts")))
+                        .setFilter(new AggregateFilterData.Builder()
+                                .setAttributionFilterMap(triggerDataFilter1).build())
+                        .setNotFilter(new AggregateFilterData.Builder()
+                                .setAttributionFilterMap(triggerDataNotFilter1).build())
+                        .build());
+        // Apply this key_piece to "geoValue".
+        triggerDataList.add(
+                new AggregateTriggerData.Builder()
+                        .setKey(new AttributionAggregatableKey.Builder()
+                                .setHighBits(0L).setLowBits(2688L).build())
+                        .setSourceKeys(new HashSet<>(Arrays.asList("geoValue", "nonMatch")))
+                        .build());
+
+        Map<String, Integer> values = new HashMap<>();
+        values.put("campaignCounts", 32768);
+        values.put("geoValue", 1664);
+        values.put("thirdSource", 100);
+        AggregatableAttributionTrigger attributionTrigger =
+                new AggregatableAttributionTrigger.Builder()
+                        .setTriggerData(triggerDataList)
+                        .setValues(values).build();
+
+        Optional<CleartextAggregatePayload> attributionReport =
+                AggregatePayloadGenerator.generateAttributionReport(
+                        attributionSource, attributionTrigger);
+        assertTrue(attributionReport.isPresent());
+        List<AggregateHistogramContribution> contributions =
+                attributionReport.get().getAggregateAttributionData().getContributions();
+
+        assertEquals(contributions.size(), 2);
+        assertTrue(contributions.contains(
+                new AggregateHistogramContribution.Builder()
+                        .setKey(BigInteger.valueOf(1369L)).setValue(32768).build()));
+        assertTrue(contributions.contains(
+                new AggregateHistogramContribution.Builder()
+                        .setKey(BigInteger.valueOf(2693L)).setValue(1664).build()));
+    }
+
+    @Test
+    public void testGenerateAttributionReportMoreTriggerDataSuccessfully() {
+        // Build AggregatableAttributionSource.
+        Map<String, AttributionAggregatableKey> aggregatableSource = new HashMap<>();
+        aggregatableSource.put("campaignCounts",
+                new AttributionAggregatableKey.Builder().setHighBits(0L).setLowBits(345L).build());
+        aggregatableSource.put("geoValue",
+                new AttributionAggregatableKey.Builder().setHighBits(0L).setLowBits(5L).build());
+        Map<String, List<String>> sourceFilterMap = new HashMap<>();
+        sourceFilterMap.put("conversion_subdomain",
+                Collections.singletonList("electronics.megastore"));
+        sourceFilterMap.put("product", Arrays.asList("1234", "234"));
+        sourceFilterMap.put("ctid", Collections.singletonList("id"));
+        AggregateFilterData sourceFilter =  new AggregateFilterData.Builder()
+                .setAttributionFilterMap(sourceFilterMap).build();
+        AggregatableAttributionSource attributionSource =
+                new AggregatableAttributionSource.Builder()
+                        .setAggregatableSource(aggregatableSource)
+                        .setAggregateFilterData(sourceFilter).build();
+        // Build AggregatableAttributionTrigger.
+        List<AggregateTriggerData> triggerDataList = new ArrayList<>();
+        // Apply this key_piece to "campaignCounts".
+        Map<String, List<String>> triggerDataFilter1 = new HashMap<>();
+        triggerDataFilter1.put("product", Collections.singletonList("1234"));
+        triggerDataFilter1.put("ctid", Collections.singletonList("id"));
+        Map<String, List<String>> triggerDataNotFilter1 = new HashMap<>();
+        triggerDataNotFilter1.put("product", Collections.singletonList("100"));
+        triggerDataList.add(
+                new AggregateTriggerData.Builder()
+                        .setKey(new AttributionAggregatableKey.Builder()
+                                .setHighBits(0L).setLowBits(1024L).build())
+                        .setSourceKeys(new HashSet<>(Collections.singletonList("campaignCounts")))
+                        .setFilter(new AggregateFilterData.Builder()
+                                .setAttributionFilterMap(triggerDataFilter1).build())
+                        .setNotFilter(new AggregateFilterData.Builder()
+                                .setAttributionFilterMap(triggerDataNotFilter1).build())
+                        .build());
+        // Apply this key_piece to "geoValue".
+        triggerDataList.add(
+                new AggregateTriggerData.Builder()
+                        .setKey(new AttributionAggregatableKey.Builder()
+                                .setHighBits(0L).setLowBits(2688L).build())
+                        .setSourceKeys(new HashSet<>(Arrays.asList("geoValue", "nonMatch")))
+                        .build());
+        // Apply this key_piece to "geoValue".
+        triggerDataList.add(
+                new AggregateTriggerData.Builder()
+                        .setKey(new AttributionAggregatableKey.Builder()
+                                .setHighBits(0L).setLowBits(768L).build())
+                        .setSourceKeys(new HashSet<>(Collections.singletonList("geoValue")))
+                        .build());
+        // Don't apply this key_piece.
+        Map<String, List<String>> triggerDataFilter2 = new HashMap<>();
+        triggerDataFilter2.put("product", Collections.singletonList("0"));
+        triggerDataList.add(
+                new AggregateTriggerData.Builder()
+                        .setKey(new AttributionAggregatableKey.Builder()
+                                .setHighBits(0L).setLowBits(200L).build())
+                        .setSourceKeys(new HashSet<>(Arrays.asList("campaignCounts", "geoValue")))
+                        .setFilter(new AggregateFilterData.Builder()
+                                .setAttributionFilterMap(triggerDataFilter2).build())
+                        .build());
+
+        Map<String, Integer> values = new HashMap<>();
+        values.put("campaignCounts", 32768);
+        values.put("geoValue", 1664);
+        AggregatableAttributionTrigger attributionTrigger =
+                new AggregatableAttributionTrigger.Builder()
+                        .setTriggerData(triggerDataList)
+                        .setValues(values).build();
+
+        Optional<CleartextAggregatePayload> attributionReport =
+                AggregatePayloadGenerator.generateAttributionReport(
+                        attributionSource, attributionTrigger);
+        assertTrue(attributionReport.isPresent());
+        List<AggregateHistogramContribution> contributions =
+                attributionReport.get().getAggregateAttributionData().getContributions();
+
+        assertEquals(contributions.size(), 2);
+        assertTrue(contributions.contains(
+                new AggregateHistogramContribution.Builder()
+                        .setKey(BigInteger.valueOf(1369L)).setValue(32768).build()));
+        assertTrue(contributions.contains(
+                new AggregateHistogramContribution.Builder()
+                        .setKey(BigInteger.valueOf(3461L)).setValue(1664).build()));
+    }
+}
diff --git a/adservices/tests/unittest/service-core/src/com/android/adservices/service/measurement/aggregation/AggregateReportTest.java b/adservices/tests/unittest/service-core/src/com/android/adservices/service/measurement/aggregation/AggregatePayloadTest.java
similarity index 71%
rename from adservices/tests/unittest/service-core/src/com/android/adservices/service/measurement/aggregation/AggregateReportTest.java
rename to adservices/tests/unittest/service-core/src/com/android/adservices/service/measurement/aggregation/AggregatePayloadTest.java
index 8a8d40f..67f2155 100644
--- a/adservices/tests/unittest/service-core/src/com/android/adservices/service/measurement/aggregation/AggregateReportTest.java
+++ b/adservices/tests/unittest/service-core/src/com/android/adservices/service/measurement/aggregation/AggregatePayloadTest.java
@@ -26,19 +26,19 @@
 import java.util.Arrays;
 import java.util.List;
 
-/** Unit tests for {@link AggregateReport} */
+/** Unit tests for {@link AggregatePayload} */
 @SmallTest
-public final class AggregateReportTest {
+public final class AggregatePayloadTest {
 
-    private AggregateReport.AggregationServicePayload createPayload(
+    private AggregatePayload.AggregationServicePayload createPayload(
             List<Integer> payload, String keyId) {
-        return new AggregateReport.AggregationServicePayload.Builder()
-                    .setPayload(payload)
-                    .setKeyId(keyId).build();
+        return new AggregatePayload.AggregationServicePayload.Builder()
+                .setPayload(payload)
+                .setKeyId(keyId).build();
     }
 
-    private AggregateReport createAggregateReport() {
-        return new AggregateReport.Builder()
+    private AggregatePayload createAggregateReport() {
+        return new AggregatePayload.Builder()
                 .setAggregationServicePayload(
                         Arrays.asList(
                                 createPayload(Arrays.asList(1, 2), "1"),
@@ -49,9 +49,10 @@
 
     @Test
     public void testCreation() throws Exception {
-        AggregateReport aggregateReport = createAggregateReport();
+        AggregatePayload aggregateReport = createAggregateReport();
         assertEquals("share_info", aggregateReport.getSharedInfo());
-        List<AggregateReport.AggregationServicePayload> payloads = aggregateReport.getPayloads();
+        List<AggregatePayload.AggregationServicePayload> payloads =
+                aggregateReport.getPayloads();
         assertEquals(payloads.size(), 2);
         assertEquals("1", payloads.get(0).getKeyId());
         assertEquals("2", payloads.get(1).getKeyId());
@@ -59,7 +60,7 @@
 
     @Test
     public void testDefaults() throws Exception {
-        AggregateReport aggregateReport = new AggregateReport.Builder().build();
+        AggregatePayload aggregateReport = new AggregatePayload.Builder().build();
         assertEquals(0, aggregateReport.getPayloads().size());
         assertNull(aggregateReport.getSharedInfo());
     }
diff --git a/adservices/tests/unittest/service-core/src/com/android/adservices/service/measurement/aggregation/AggregateTriggerDataTest.java b/adservices/tests/unittest/service-core/src/com/android/adservices/service/measurement/aggregation/AggregateTriggerDataTest.java
index 4d390de..35be4d1 100644
--- a/adservices/tests/unittest/service-core/src/com/android/adservices/service/measurement/aggregation/AggregateTriggerDataTest.java
+++ b/adservices/tests/unittest/service-core/src/com/android/adservices/service/measurement/aggregation/AggregateTriggerDataTest.java
@@ -17,14 +17,19 @@
 package com.android.adservices.service.measurement.aggregation;
 
 import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
 
 import androidx.test.filters.SmallTest;
 
 import org.junit.Test;
 
 import java.util.Arrays;
+import java.util.HashMap;
 import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
 
 /** Unit tests for {@link AggregateTriggerData} */
 @SmallTest
@@ -32,6 +37,12 @@
 
     @Test
     public void testCreation() throws Exception {
+        Map<String, List<String>> attributionFilterMap = new HashMap<>();
+        attributionFilterMap.put("ctid", Arrays.asList("1", "2"));
+        AggregateFilterData filterData =
+                new AggregateFilterData.Builder()
+                        .setAttributionFilterMap(attributionFilterMap).build();
+
         AggregateTriggerData attributionTriggerData =
                 new AggregateTriggerData.Builder()
                         .setKey(
@@ -39,11 +50,15 @@
                                         .setHighBits(0L).setLowBits(5L).build())
                         .setSourceKeys(new HashSet<>(
                                 Arrays.asList("campCounts", "campGeoCounts", "campGeoValue")))
+                        .setFilter(filterData)
                         .build();
 
         assertEquals(attributionTriggerData.getKey().getHighBits().longValue(), 0L);
         assertEquals(attributionTriggerData.getKey().getLowBits().longValue(), 5L);
         assertEquals(attributionTriggerData.getSourceKeys().size(), 3);
+        assertTrue(attributionTriggerData.getFilter().isPresent());
+        AggregateFilterData data = attributionTriggerData.getFilter().get();
+        assertEquals(2, data.getAttributionFilterMap().get("ctid").size());
     }
 
     @Test
@@ -52,5 +67,7 @@
                 new AggregateTriggerData.Builder().build();
         assertNull(attributionTriggerData.getKey());
         assertEquals(attributionTriggerData.getSourceKeys().size(), 0);
+        assertFalse(attributionTriggerData.getFilter().isPresent());
+        assertFalse(attributionTriggerData.getNotFilter().isPresent());
     }
 }
diff --git a/adservices/tests/unittest/service-core/src/com/android/adservices/service/measurement/aggregation/AttributionReportTest.java b/adservices/tests/unittest/service-core/src/com/android/adservices/service/measurement/aggregation/CleartextAggregatePayloadTest.java
similarity index 65%
rename from adservices/tests/unittest/service-core/src/com/android/adservices/service/measurement/aggregation/AttributionReportTest.java
rename to adservices/tests/unittest/service-core/src/com/android/adservices/service/measurement/aggregation/CleartextAggregatePayloadTest.java
index 0179969..ed20a64 100644
--- a/adservices/tests/unittest/service-core/src/com/android/adservices/service/measurement/aggregation/AttributionReportTest.java
+++ b/adservices/tests/unittest/service-core/src/com/android/adservices/service/measurement/aggregation/CleartextAggregatePayloadTest.java
@@ -24,41 +24,45 @@
 
 import org.junit.Test;
 
-/** Unit tests for {@link AttributionReport} */
+/** Unit tests for {@link CleartextAggregatePayload} */
 @SmallTest
-public final class AttributionReportTest {
+public final class CleartextAggregatePayloadTest {
 
-    private AttributionReport.AttributionInfo createAttributionInfo() {
-        return new AttributionReport.AttributionInfo.Builder()
-                .setTime(1000L).setDebugkey(null).build();
+    private CleartextAggregatePayload.AttributionInfo createAttributionInfo() {
+        return new CleartextAggregatePayload.AttributionInfo.Builder().setTime(1000L).build();
     }
-    private AttributionReport createAttributionReport() {
-        return new AttributionReport.Builder()
+
+    private CleartextAggregatePayload createAttributionReport() {
+        return new CleartextAggregatePayload.Builder()
                 .setAttributionInfo(createAttributionInfo())
                 .setReportTime(1L)
                 .setExternalReportId(2L)
                 .setAggregateAttributionData(
                         new AggregateAttributionData.Builder().build())
+                .setStatus(CleartextAggregatePayload.Status.PENDING)
                 .build();
     }
 
     @Test
     public void testCreation() throws Exception {
-        AttributionReport attributionReport = createAttributionReport();
+        CleartextAggregatePayload attributionReport = createAttributionReport();
         assertEquals(1L, attributionReport.getReportTime());
         assertEquals(2L, attributionReport.getExternalReportId());
         assertNotNull(attributionReport.getAggregateAttributionData());
-        AttributionReport.AttributionInfo attributionInfo = attributionReport.getAttributionInfo();
+        CleartextAggregatePayload.AttributionInfo attributionInfo =
+                attributionReport.getAttributionInfo();
         assertEquals(1000L, attributionInfo.getTime());
-        assertNull(attributionInfo.getDebugkey());
+        assertEquals(CleartextAggregatePayload.Status.PENDING, attributionReport.getStatus());
     }
 
     @Test
     public void testDefaults() throws Exception {
-        AttributionReport attributionReport = new AttributionReport.Builder().build();
+        CleartextAggregatePayload attributionReport =
+                new CleartextAggregatePayload.Builder().build();
         assertNull(attributionReport.getAttributionInfo());
         assertEquals(0L, attributionReport.getReportTime());
         assertEquals(0L, attributionReport.getExternalReportId());
         assertNull(attributionReport.getAggregateAttributionData());
+        assertEquals(CleartextAggregatePayload.Status.PENDING, attributionReport.getStatus());
     }
 }