1 /*
2  * Copyright (C) 2023 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package android.adservices.adselection;
18 
19 import static android.adservices.adselection.AdSelectionOutcome.UNSET_AD_SELECTION_ID;
20 import static android.adservices.adselection.AdSelectionOutcome.UNSET_AD_SELECTION_ID_MESSAGE;
21 
22 import android.annotation.IntDef;
23 import android.annotation.NonNull;
24 import android.annotation.Nullable;
25 import android.view.InputEvent;
26 
27 import com.android.internal.util.Preconditions;
28 
29 import java.lang.annotation.Retention;
30 import java.lang.annotation.RetentionPolicy;
31 import java.nio.charset.StandardCharsets;
32 import java.util.Locale;
33 import java.util.Objects;
34 
35 /**
36  * Request object wrapping the required arguments needed to report an ad event.
37  */
38 public class ReportEventRequest {
39     public static final int FLAG_REPORTING_DESTINATION_SELLER = 1 << 0;
40     public static final int FLAG_REPORTING_DESTINATION_BUYER = 1 << 1;
41     private static final int UNSET_REPORTING_DESTINATIONS = 0;
42     private static final String UNSET_REPORTING_DESTINATIONS_MESSAGE =
43             "Reporting destinations bitfield not set.";
44     private static final String INVALID_REPORTING_DESTINATIONS_MESSAGE =
45             "Invalid reporting destinations bitfield!";
46 
47     /** @hide */
48     public static final long REPORT_EVENT_MAX_INTERACTION_DATA_SIZE_B = 64 * 1024; // 64 KB
49 
50     private static final String EVENT_DATA_SIZE_MAX_EXCEEDED =
51             "Event data should not exceed " + REPORT_EVENT_MAX_INTERACTION_DATA_SIZE_B + " bytes";
52 
53     private final long mAdSelectionId;
54     @NonNull private final String mEventKey;
55     @Nullable private final InputEvent mInputEvent;
56     @NonNull private final String mEventData;
57     @ReportingDestination private final int mReportingDestinations; // buyer, seller, or both
58 
ReportEventRequest(@onNull Builder builder)59     private ReportEventRequest(@NonNull Builder builder) {
60         Objects.requireNonNull(builder);
61 
62         Preconditions.checkArgument(
63                 builder.mAdSelectionId != UNSET_AD_SELECTION_ID, UNSET_AD_SELECTION_ID_MESSAGE);
64         Preconditions.checkArgument(
65                 builder.mReportingDestinations != UNSET_REPORTING_DESTINATIONS,
66                 UNSET_REPORTING_DESTINATIONS_MESSAGE);
67         Preconditions.checkArgument(
68                 isValidDestination(builder.mReportingDestinations),
69                 INVALID_REPORTING_DESTINATIONS_MESSAGE);
70         Preconditions.checkArgument(
71                 builder.mEventData.getBytes(StandardCharsets.UTF_8).length
72                         <= REPORT_EVENT_MAX_INTERACTION_DATA_SIZE_B,
73                 EVENT_DATA_SIZE_MAX_EXCEEDED);
74 
75         this.mAdSelectionId = builder.mAdSelectionId;
76         this.mEventKey = builder.mEventKey;
77         this.mInputEvent = builder.mInputEvent;
78         this.mEventData = builder.mEventData;
79         this.mReportingDestinations = builder.mReportingDestinations;
80     }
81 
82     /**
83      * Returns the adSelectionId, the primary identifier of an ad selection process.
84      */
getAdSelectionId()85     public long getAdSelectionId() {
86         return mAdSelectionId;
87     }
88 
89     /**
90      * Returns the event key, the type of ad event to be reported.
91      *
92      * <p>This field will be used to fetch the {@code reportingUri} associated with the {@code
93      * eventKey} registered in {@code registerAdBeacon} after ad selection.
94      *
95      * <p>This field should be an exact match to the {@code eventKey} registered in {@code
96      * registerAdBeacon}. Specific details about {@code registerAdBeacon} can be found at the
97      * documentation of {@link AdSelectionManager#reportImpression}
98      *
99      * <p>The event key (when inspecting its byte array with {@link String#getBytes()}) in {@code
100      * UTF-8} format should not exceed 40 bytes. Any key exceeding this limit will not be registered
101      * during the {@code registerAdBeacon} call.
102      */
103     @NonNull
getKey()104     public String getKey() {
105         return mEventKey;
106     }
107 
108     /**
109      * Returns the input event associated with the user interaction.
110      *
111      * <p>This field is either {@code null}, representing a <em>view</em> event, or has an {@link
112      * InputEvent} object, representing a <em>click</em> event.
113      */
114     @Nullable
getInputEvent()115     public InputEvent getInputEvent() {
116         return mInputEvent;
117     }
118 
119     /**
120      * Returns the ad event data.
121      *
122      * <p>After ad selection, this data is generated by the caller. The caller can then call {@link
123      * AdSelectionManager#reportEvent}. This data will be attached in a POST request to the {@code
124      * reportingUri} registered in {@code registerAdBeacon}.
125      *
126      * <p>The size of {@link String#getBytes()} in {@code UTF-8} format should be below 64KB.
127      */
128     @NonNull
getData()129     public String getData() {
130         return mEventData;
131     }
132 
133     /**
134      * Returns the bitfield of reporting destinations to report to (buyer, seller, or both).
135      *
136      * <p>To create this bitfield, place an {@code |} bitwise operator between each {@code
137      * reportingDestination} to be reported to. For example to only report to buyer, set the
138      * reportingDestinations field to {@link #FLAG_REPORTING_DESTINATION_BUYER} To only report to
139      * seller, set the reportingDestinations field to {@link #FLAG_REPORTING_DESTINATION_SELLER} To
140      * report to both buyers and sellers, set the reportingDestinations field to {@link
141      * #FLAG_REPORTING_DESTINATION_BUYER} | {@link #FLAG_REPORTING_DESTINATION_SELLER}
142      */
143     @ReportingDestination
getReportingDestinations()144     public int getReportingDestinations() {
145         return mReportingDestinations;
146     }
147 
148     /** @hide */
149     @IntDef(
150             flag = true,
151             prefix = {"FLAG_REPORTING_DESTINATION"},
152             value = {FLAG_REPORTING_DESTINATION_SELLER, FLAG_REPORTING_DESTINATION_BUYER})
153     @Retention(RetentionPolicy.SOURCE)
154     public @interface ReportingDestination {}
155 
isValidDestination(@eportingDestination int reportingDestinations)156     private static boolean isValidDestination(@ReportingDestination int reportingDestinations) {
157         return 0 < reportingDestinations
158                 && reportingDestinations
159                         <= (FLAG_REPORTING_DESTINATION_SELLER | FLAG_REPORTING_DESTINATION_BUYER);
160     }
161 
162     /** Builder for {@link ReportEventRequest} objects. */
163     public static final class Builder {
164 
165         private long mAdSelectionId;
166         @NonNull private String mEventKey;
167         @Nullable private InputEvent mInputEvent;
168         @NonNull private String mEventData;
169         @ReportingDestination private int mReportingDestinations; // buyer, seller, or both
170 
Builder( long adSelectionId, @NonNull String eventKey, @NonNull String eventData, @ReportingDestination int reportingDestinations)171         public Builder(
172                 long adSelectionId,
173                 @NonNull String eventKey,
174                 @NonNull String eventData,
175                 @ReportingDestination int reportingDestinations) {
176             Objects.requireNonNull(eventKey);
177             Objects.requireNonNull(eventData);
178 
179             Preconditions.checkArgument(
180                     adSelectionId != UNSET_AD_SELECTION_ID, UNSET_AD_SELECTION_ID_MESSAGE);
181             Preconditions.checkArgument(
182                     reportingDestinations != UNSET_REPORTING_DESTINATIONS,
183                     UNSET_REPORTING_DESTINATIONS_MESSAGE);
184             Preconditions.checkArgument(
185                     isValidDestination(reportingDestinations),
186                     INVALID_REPORTING_DESTINATIONS_MESSAGE);
187             Preconditions.checkArgument(
188                     eventData.getBytes(StandardCharsets.UTF_8).length
189                             <= REPORT_EVENT_MAX_INTERACTION_DATA_SIZE_B,
190                     EVENT_DATA_SIZE_MAX_EXCEEDED);
191 
192             this.mAdSelectionId = adSelectionId;
193             this.mEventKey = eventKey;
194             this.mEventData = eventData;
195             this.mReportingDestinations = reportingDestinations;
196         }
197 
198         /**
199          * Sets the ad selection ID with which the rendered ad's events are associated.
200          *
201          * <p>See {@link #getAdSelectionId()} for more information.
202          */
203         @NonNull
setAdSelectionId(long adSelectionId)204         public Builder setAdSelectionId(long adSelectionId) {
205             Preconditions.checkArgument(
206                     adSelectionId != UNSET_AD_SELECTION_ID, UNSET_AD_SELECTION_ID_MESSAGE);
207             mAdSelectionId = adSelectionId;
208             return this;
209         }
210 
211         /**
212          * Sets the event key, the type of ad event to be reported.
213          *
214          * <p>See {@link #getKey()} for more information.
215          */
216         @NonNull
setKey(@onNull String eventKey)217         public Builder setKey(@NonNull String eventKey) {
218             Objects.requireNonNull(eventKey);
219 
220             mEventKey = eventKey;
221             return this;
222         }
223 
224         /**
225          * Sets the input event associated with the user interaction.
226          *
227          * <p>See {@link #getInputEvent()} for more information.
228          */
229         @NonNull
setInputEvent(@ullable InputEvent inputEvent)230         public Builder setInputEvent(@Nullable InputEvent inputEvent) {
231             mInputEvent = inputEvent;
232             return this;
233         }
234 
235         /**
236          * Sets the ad event data.
237          *
238          * <p>See {@link #getData()} for more information.
239          */
240         @NonNull
setData(@onNull String eventData)241         public Builder setData(@NonNull String eventData) {
242             Objects.requireNonNull(eventData);
243 
244             mEventData = eventData;
245             return this;
246         }
247 
248         /**
249          * Sets the bitfield of reporting destinations to report to (buyer, seller, or both).
250          *
251          * <p>See {@link #getReportingDestinations()} for more information.
252          */
253         @NonNull
setReportingDestinations(@eportingDestination int reportingDestinations)254         public Builder setReportingDestinations(@ReportingDestination int reportingDestinations) {
255             Preconditions.checkArgument(
256                     isValidDestination(reportingDestinations),
257                     INVALID_REPORTING_DESTINATIONS_MESSAGE);
258 
259             mReportingDestinations = reportingDestinations;
260             return this;
261         }
262 
263         /** Builds the {@link ReportEventRequest} object. */
264         @NonNull
build()265         public ReportEventRequest build() {
266             return new ReportEventRequest(this);
267         }
268     }
269 }
270