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