• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2022 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 com.android.adservices.data.adselection;
18 
19 import android.adservices.adselection.ReportEventRequest;
20 import android.adservices.common.AdSelectionSignals;
21 import android.adservices.common.AdTechIdentifier;
22 import android.net.Uri;
23 
24 import androidx.annotation.NonNull;
25 import androidx.annotation.Nullable;
26 import androidx.room.Dao;
27 import androidx.room.Insert;
28 import androidx.room.OnConflictStrategy;
29 import androidx.room.Query;
30 import androidx.room.Transaction;
31 
32 import com.android.adservices.LoggerFactory;
33 import com.android.adservices.data.adselection.datahandlers.AdSelectionInitialization;
34 import com.android.adservices.data.adselection.datahandlers.AdSelectionResultBidAndUri;
35 import com.android.adservices.data.adselection.datahandlers.DBValidator;
36 import com.android.adservices.data.adselection.datahandlers.RegisteredAdInteraction;
37 import com.android.adservices.data.adselection.datahandlers.ReportingComputationData;
38 import com.android.adservices.data.adselection.datahandlers.ReportingData;
39 import com.android.adservices.data.adselection.datahandlers.WinningCustomAudience;
40 
41 import java.time.Instant;
42 import java.util.List;
43 import java.util.Objects;
44 import java.util.stream.Collectors;
45 
46 /**
47  * Data Access Object interface for access to the local AdSelection data storage.
48  *
49  * <p>Annotation will generate Room based SQLite Dao implementation.
50  */
51 @Dao
52 public abstract class AdSelectionEntryDao {
53     private static final LoggerFactory.Logger sLogger = LoggerFactory.getFledgeLogger();
54 
55     /**
56      * Add a new successful ad selection entry into the table ad_selection.
57      *
58      * @param adSelection is the AdSelection to add to the table ad_selection if the ad_selection_id
59      *     not exists.
60      */
61     // TODO(b/230568647): retry adSelectionId generation in case of collision
62     @Insert(onConflict = OnConflictStrategy.ABORT)
persistAdSelection(DBAdSelection adSelection)63     public abstract void persistAdSelection(DBAdSelection adSelection);
64 
65     /**
66      * Write a buyer decision logic entry into the table buyer_decision_logic.
67      *
68      * @param buyerDecisionLogic is the BuyerDecisionLogic to write to table buyer_decision_logic.
69      */
70     @Insert(onConflict = OnConflictStrategy.REPLACE)
persistBuyerDecisionLogic(DBBuyerDecisionLogic buyerDecisionLogic)71     public abstract void persistBuyerDecisionLogic(DBBuyerDecisionLogic buyerDecisionLogic);
72 
73     /**
74      * Add an ad selection override into the table ad_selection_overrides
75      *
76      * @param adSelectionOverride is the AdSelectionOverride to add to table ad_selection_overrides.
77      *     If a {@link DBAdSelectionOverride} object with the {@code adSelectionConfigId} already
78      *     exists, this will replace the existing object.
79      */
80     @Insert(onConflict = OnConflictStrategy.REPLACE)
persistAdSelectionOverride(DBAdSelectionOverride adSelectionOverride)81     public abstract void persistAdSelectionOverride(DBAdSelectionOverride adSelectionOverride);
82 
83     /**
84      * Add an ad selection override for Buyers' decision logic
85      *
86      * @param perBuyerDecisionLogicOverride is an override for the
87      *     ad_selection_buyer_logic_overrides. If a {@link DBBuyerDecisionOverride} object with the
88      *     {@code adSelectionConfigId} already exists, this will replace the existing object.
89      */
90     @Insert(onConflict = OnConflictStrategy.REPLACE)
persistPerBuyerDecisionLogicOverride( List<DBBuyerDecisionOverride> perBuyerDecisionLogicOverride)91     public abstract void persistPerBuyerDecisionLogicOverride(
92             List<DBBuyerDecisionOverride> perBuyerDecisionLogicOverride);
93 
94     /**
95      * Adds a list of registered ad interactions to the table registered_ad_interactions
96      *
97      * <p>This method is not meant to be used on its own, since it doesn't take into account the
98      * maximum size of {@code registered_ad_interactions}. Use {@link
99      * #safelyInsertRegisteredAdInteractions(long, List, long, long, int)} instead.
100      *
101      * @param registeredAdInteractions is the list of {@link DBRegisteredAdInteraction} objects to
102      *     write to the table registered_ad_interactions.
103      */
104     @Insert(onConflict = OnConflictStrategy.REPLACE)
persistDBRegisteredAdInteractions( List<DBRegisteredAdInteraction> registeredAdInteractions)105     protected abstract void persistDBRegisteredAdInteractions(
106             List<DBRegisteredAdInteraction> registeredAdInteractions);
107 
108     /**
109      * Checks if there is a row in the ad selection data with the unique key ad_selection_id in on
110      * device auction tables
111      *
112      * @param adSelectionId which is the key to query the corresponding ad selection data.
113      * @return true if row exists, false otherwise
114      */
115     @Query(
116             "SELECT EXISTS(SELECT 1 FROM ad_selection WHERE ad_selection_id = :adSelectionId LIMIT"
117                     + " 1)")
doesAdSelectionIdExist(long adSelectionId)118     public abstract boolean doesAdSelectionIdExist(long adSelectionId);
119 
120     /**
121      * Checks if there is a row in the buyer decision logic data with the unique key
122      * bidding_logic_uri
123      *
124      * @param biddingLogicUri which is the key to query the corresponding buyer decision logic data.
125      * @return true if row exists, false otherwise
126      */
127     @Query(
128             "SELECT EXISTS(SELECT 1 FROM buyer_decision_logic WHERE bidding_logic_uri ="
129                     + " :biddingLogicUri LIMIT 1)")
doesBuyerDecisionLogicExist(Uri biddingLogicUri)130     public abstract boolean doesBuyerDecisionLogicExist(Uri biddingLogicUri);
131 
132     /**
133      * Checks if there is a row in the ad selection override data with the unique key
134      * ad_selection_config_id
135      *
136      * @param adSelectionConfigId which is the key to query the corresponding ad selection override
137      *     data.
138      * @return true if row exists, false otherwise
139      */
140     @Query(
141             "SELECT EXISTS(SELECT 1 FROM ad_selection_overrides WHERE ad_selection_config_id ="
142                     + " :adSelectionConfigId AND app_package_name = :appPackageName LIMIT 1)")
doesAdSelectionOverrideExistForPackageName( String adSelectionConfigId, String appPackageName)143     public abstract boolean doesAdSelectionOverrideExistForPackageName(
144             String adSelectionConfigId, String appPackageName);
145 
146     /**
147      * Checks if there is a row in the {@link DBReportingComputationInfo} table with the unique key
148      * ad_selection_id in on device auction tables
149      *
150      * @param adSelectionId which is the key to query the corresponding ad selection data.
151      * @return true if row exists, false otherwise
152      */
153     @Query(
154             "SELECT EXISTS(SELECT 1 FROM reporting_computation_info WHERE ad_selection_id ="
155                     + " :adSelectionId LIMIT 1)")
doesReportingComputationInfoExist(long adSelectionId)156     public abstract boolean doesReportingComputationInfoExist(long adSelectionId);
157 
158     /**
159      * Checks if there is a row in the registered_ad_interactions table that matches the primary key
160      * combination of adSelectionId, interactionKey, and destination
161      *
162      * @param adSelectionId serves as the primary key denoting the ad selection process this entry
163      *     id associated with
164      * @param interactionKey the interaction key
165      * @param destination denotes buyer, seller, etc.
166      */
167     @Query(
168             "SELECT EXISTS(SELECT 1 FROM registered_ad_interactions WHERE ad_selection_id ="
169                     + " :adSelectionId AND interaction_key = :interactionKey AND destination"
170                     + " = :destination LIMIT 1)")
doesRegisteredAdInteractionExist( long adSelectionId, String interactionKey, @ReportEventRequest.ReportingDestination int destination)171     public abstract boolean doesRegisteredAdInteractionExist(
172             long adSelectionId,
173             String interactionKey,
174             @ReportEventRequest.ReportingDestination int destination);
175 
176     /**
177      * Get the ad selection entry by its unique key ad_selection_id.
178      *
179      * @param adSelectionId which is the key to query the corresponding ad selection entry.
180      * @return an {@link DBAdSelectionEntry} if exists.
181      */
182     @Query(
183             "SELECT ad_selection.ad_selection_id as ad_selection_id,"
184                     + " ad_selection.custom_audience_signals_owner as "
185                     + "custom_audience_signals_owner,"
186                     + " ad_selection.custom_audience_signals_buyer as "
187                     + "custom_audience_signals_buyer,"
188                     + " ad_selection.custom_audience_signals_name as custom_audience_signals_name,"
189                     + " ad_selection.custom_audience_signals_activation_time as"
190                     + " custom_audience_signals_activation_time,"
191                     + " ad_selection.custom_audience_signals_expiration_time as"
192                     + " custom_audience_signals_expiration_time,"
193                     + " ad_selection.custom_audience_signals_user_bidding_signals as"
194                     + " custom_audience_signals_user_bidding_signals, ad_selection"
195                     + ".contextual_signals"
196                     + " as contextual_signals,ad_selection.winning_ad_render_uri as"
197                     + " winning_ad_render_uri,ad_selection.winning_ad_bid as"
198                     + " winning_ad_bid,ad_selection.creation_timestamp as"
199                     + " creation_timestamp,buyer_decision_logic.buyer_decision_logic_js as"
200                     + " buyer_decision_logic_js, ad_selection.bidding_logic_uri as "
201                     + "bidding_logic_uri,"
202                     + " ad_selection.seller_contextual_signals as seller_contextual_signals FROM"
203                     + " ad_selection LEFT JOIN buyer_decision_logic ON ad_selection"
204                     + ".bidding_logic_uri ="
205                     + " buyer_decision_logic.bidding_logic_uri WHERE ad_selection.ad_selection_id ="
206                     + " :adSelectionId")
getAdSelectionEntityById(long adSelectionId)207     public abstract DBAdSelectionEntry getAdSelectionEntityById(long adSelectionId);
208 
209     /**
210      * Get the {@link DBReportingComputationInfo} by its unique key ad_selection_id.
211      *
212      * @param adSelectionId which is the key to query the corresponding ad selection entry.
213      * @return an {@link DBReportingComputationInfo} if exists.
214      */
215     @Query(
216             "SELECT reporting_computation_info.ad_selection_id as ad_selection_id,"
217                     + " reporting_computation_info.bidding_logic_uri as bidding_logic_uri,"
218                     + " reporting_computation_info.buyer_decision_logic_js as "
219                     + "buyer_decision_logic_js,"
220                     + " reporting_computation_info.seller_contextual_signals as"
221                     + " seller_contextual_signals, reporting_computation_info"
222                     + ".buyer_contextual_signals"
223                     + " as buyer_contextual_signals,"
224                     + " reporting_computation_info.custom_audience_signals_owner as"
225                     + " custom_audience_signals_owner,"
226                     + " reporting_computation_info.custom_audience_signals_buyer as"
227                     + " custom_audience_signals_buyer,"
228                     + " reporting_computation_info.custom_audience_signals_name as"
229                     + " custom_audience_signals_name,"
230                     + " reporting_computation_info.custom_audience_signals_activation_time as"
231                     + " custom_audience_signals_activation_time,"
232                     + " reporting_computation_info.custom_audience_signals_expiration_time as"
233                     + " custom_audience_signals_expiration_time,"
234                     + " reporting_computation_info.custom_audience_signals_user_bidding_signals as"
235                     + " custom_audience_signals_user_bidding_signals,"
236                     + " reporting_computation_info.winning_ad_bid as winning_ad_bid,"
237                     + " reporting_computation_info.winning_ad_render_uri as winning_ad_render_uri"
238                     + "  FROM"
239                     + " reporting_computation_info WHERE reporting_computation_info"
240                     + ".ad_selection_id ="
241                     + " :adSelectionId")
getReportingComputationInfoById(long adSelectionId)242     public abstract DBReportingComputationInfo getReportingComputationInfoById(long adSelectionId);
243 
244     /**
245      * Get the ad selection entries with a batch of ad_selection_ids.
246      *
247      * @param adSelectionIds are the list of keys to query the corresponding ad selection entries.
248      * @return ad selection entries if exists.
249      */
250     @Query(
251             "SELECT ad_selection.ad_selection_id AS"
252                     + " ad_selection_id,ad_selection.custom_audience_signals_owner as"
253                     + " custom_audience_signals_owner, ad_selection.custom_audience_signals_buyer"
254                     + " as"
255                     + " custom_audience_signals_buyer, ad_selection.custom_audience_signals_name as"
256                     + " custom_audience_signals_name,"
257                     + " ad_selection.custom_audience_signals_activation_time as"
258                     + " custom_audience_signals_activation_time,"
259                     + " ad_selection.custom_audience_signals_expiration_time as"
260                     + " custom_audience_signals_expiration_time,"
261                     + " ad_selection.custom_audience_signals_user_bidding_signals as"
262                     + " custom_audience_signals_user_bidding_signals, ad_selection"
263                     + ".contextual_signals"
264                     + " AS contextual_signals,ad_selection.winning_ad_render_uri AS"
265                     + " winning_ad_render_uri,ad_selection.winning_ad_bid AS winning_ad_bid,"
266                     + " ad_selection.creation_timestamp as creation_timestamp,"
267                     + " buyer_decision_logic.buyer_decision_logic_js AS buyer_decision_logic_js,"
268                     + " ad_selection.bidding_logic_uri AS bidding_logic_uri,"
269                     + " ad_selection.seller_contextual_signals as seller_contextual_signals FROM"
270                     + " ad_selection LEFT JOIN buyer_decision_logic ON ad_selection"
271                     + ".bidding_logic_uri ="
272                     + " buyer_decision_logic.bidding_logic_uri WHERE ad_selection.ad_selection_id"
273                     + " IN"
274                     + " (:adSelectionIds) ")
getAdSelectionEntities(List<Long> adSelectionIds)275     public abstract List<DBAdSelectionEntry> getAdSelectionEntities(List<Long> adSelectionIds);
276 
277     /**
278      * Get the ad selection entries with a batch of ad_selection_ids.
279      *
280      * @param adSelectionIds are the list of keys to query the corresponding ad selection entries.
281      * @return ad selection entries if exists.
282      */
283     @Query(
284             "SELECT ad_selection.ad_selection_id AS"
285                     + " ad_selection_id,ad_selection.custom_audience_signals_owner as"
286                     + " custom_audience_signals_owner, ad_selection.custom_audience_signals_buyer"
287                     + " as"
288                     + " custom_audience_signals_buyer, ad_selection.custom_audience_signals_name as"
289                     + " custom_audience_signals_name,"
290                     + " ad_selection.custom_audience_signals_activation_time as"
291                     + " custom_audience_signals_activation_time,"
292                     + " ad_selection.custom_audience_signals_expiration_time as"
293                     + " custom_audience_signals_expiration_time,"
294                     + " ad_selection.custom_audience_signals_user_bidding_signals as"
295                     + " custom_audience_signals_user_bidding_signals, ad_selection"
296                     + ".contextual_signals"
297                     + " AS contextual_signals,ad_selection.winning_ad_render_uri AS"
298                     + " winning_ad_render_uri,ad_selection.winning_ad_bid AS winning_ad_bid,"
299                     + " ad_selection.creation_timestamp as creation_timestamp,"
300                     + " buyer_decision_logic.buyer_decision_logic_js AS buyer_decision_logic_js,"
301                     + " ad_selection.bidding_logic_uri AS bidding_logic_uri,"
302                     + " ad_selection.seller_contextual_signals as seller_contextual_signals FROM"
303                     + " ad_selection LEFT JOIN buyer_decision_logic ON ad_selection"
304                     + ".bidding_logic_uri ="
305                     + " buyer_decision_logic.bidding_logic_uri WHERE ad_selection.ad_selection_id"
306                     + " IN"
307                     + " (:adSelectionIds) AND ad_selection.caller_package_name = "
308                     + ":callerPackageName")
getAdSelectionEntities( List<Long> adSelectionIds, String callerPackageName)309     public abstract List<DBAdSelectionEntry> getAdSelectionEntities(
310             List<Long> adSelectionIds, String callerPackageName);
311 
312     /**
313      * Get ad selection JS override by its unique key and the package name of the app that created
314      * the override.
315      *
316      * @return ad selection override result if exists.
317      */
318     @Query(
319             "SELECT decision_logic FROM ad_selection_overrides WHERE ad_selection_config_id ="
320                     + " :adSelectionConfigId AND app_package_name = :appPackageName")
321     @Nullable
getDecisionLogicOverride( String adSelectionConfigId, String appPackageName)322     public abstract String getDecisionLogicOverride(
323             String adSelectionConfigId, String appPackageName);
324 
325     /**
326      * Get ad selection trusted scoring signals override by its unique key and the package name of
327      * the app that created the override.
328      *
329      * @return ad selection override result if exists.
330      */
331     @Query(
332             "SELECT trusted_scoring_signals FROM ad_selection_overrides WHERE"
333                     + " ad_selection_config_id = :adSelectionConfigId AND app_package_name ="
334                     + " :appPackageName")
335     @Nullable
getTrustedScoringSignalsOverride( String adSelectionConfigId, String appPackageName)336     public abstract String getTrustedScoringSignalsOverride(
337             String adSelectionConfigId, String appPackageName);
338 
339     /**
340      * Get ad selection buyer decision logic override by its unique key and the package name of the
341      * app that created the override.
342      *
343      * @return ad selection override result if exists.
344      */
345     @Query(
346             "SELECT * FROM ad_selection_buyer_logic_overrides WHERE"
347                     + " ad_selection_config_id = :adSelectionConfigId AND app_package_name ="
348                     + " :appPackageName")
349     @Nullable
getPerBuyerDecisionLogicOverride( String adSelectionConfigId, String appPackageName)350     public abstract List<DBBuyerDecisionOverride> getPerBuyerDecisionLogicOverride(
351             String adSelectionConfigId, String appPackageName);
352 
353     /**
354      * Gets the interaction reporting uri that was registered with the primary key combination of
355      * {@code adSelectionId}, {@code interactionKey}, and {@code destination}.
356      *
357      * @return interaction reporting uri if exists.
358      */
359     @Query(
360             "SELECT interaction_reporting_uri FROM registered_ad_interactions WHERE"
361                     + " ad_selection_id = :adSelectionId AND interaction_key = :interactionKey AND"
362                     + " destination = :destination")
363     @Nullable
getRegisteredAdInteractionUri( long adSelectionId, String interactionKey, @ReportEventRequest.ReportingDestination int destination)364     public abstract Uri getRegisteredAdInteractionUri(
365             long adSelectionId,
366             String interactionKey,
367             @ReportEventRequest.ReportingDestination int destination);
368 
369     /**
370      * List all registered ad interactions for a given auction.
371      *
372      * @param adSelectionId The id of the ad selection / auction to query.
373      * @param destination The reporting desintation (i.e. buyer or seller or other valid option).
374      * @return List of registered ad interactions (empty if none exist).
375      */
376     @Query(
377             "SELECT interaction_key as interactionKey, interaction_reporting_uri as "
378                     + "interactionReportingUri FROM registered_ad_interactions "
379                     + "WHERE ad_selection_id = :adSelectionId AND destination = :destination")
listRegisteredAdInteractions( long adSelectionId, @ReportEventRequest.ReportingDestination int destination)380     public abstract List<RegisteredAdInteraction> listRegisteredAdInteractions(
381             long adSelectionId, @ReportEventRequest.ReportingDestination int destination);
382 
383     /**
384      * Gets the {@link DBAdSelectionHistogramInfo} representing the histogram information associated
385      * with a given ad selection for on device.
386      *
387      * @return a {@link DBAdSelectionHistogramInfo} containing the histogram info associated with
388      *     the ad selection, or {@code null} if no match is found
389      */
390     @Query(
391             "SELECT custom_audience_signals_buyer, ad_counter_int_keys FROM ad_selection "
392                     + "WHERE ad_selection_id = :adSelectionId "
393                     + "AND caller_package_name = :callerPackageName")
394     @Nullable
getAdSelectionHistogramInfoInOnDeviceTable( long adSelectionId, @NonNull String callerPackageName)395     public abstract DBAdSelectionHistogramInfo getAdSelectionHistogramInfoInOnDeviceTable(
396             long adSelectionId, @NonNull String callerPackageName);
397 
398     /**
399      * Gets the {@link DBAdSelectionHistogramInfo} representing the histogram information associated
400      * with a given ad selection for server auction.
401      *
402      * @return a {@link DBAdSelectionHistogramInfo} containing the histogram info associated with
403      *     the ad selection, or {@code null} if no match is found
404      */
405     @Query(
406             "SELECT custom_audience_signals_buyer, ad_counter_int_keys "
407                     + "FROM ad_selection "
408                     + "WHERE ad_selection_id = :adSelectionId "
409                     + "AND caller_package_name = :callerPackageName "
410                     + "UNION ALL "
411                     + "SELECT winning_buyer AS custom_audience_signals_buyer, "
412                     + "winning_custom_audience_ad_counter_int_keys AS ad_counter_int_keys "
413                     + "FROM ad_selection_result results "
414                     + "JOIN ad_selection_initialization init "
415                     + "ON results.ad_selection_id = init.ad_selection_id "
416                     + "WHERE init.ad_selection_id = :adSelectionId "
417                     + "AND init.caller_package_name = :callerPackageName")
418     @Nullable
getAdSelectionHistogramInfo( long adSelectionId, @NonNull String callerPackageName)419     public abstract DBAdSelectionHistogramInfo getAdSelectionHistogramInfo(
420             long adSelectionId, @NonNull String callerPackageName);
421 
422     /**
423      * Gets the {@link DBAdSelectionHistogramInfo} representing the histogram information associated
424      * with a given ad selection. Only fetches data from the unified tables, not {@code
425      * ad_selection}
426      *
427      * @return a {@link DBAdSelectionHistogramInfo} containing the histogram info associated with
428      *     the ad selection, or {@code null} if no match is found
429      */
430     @Query(
431             "SELECT winning_buyer AS custom_audience_signals_buyer, "
432                     + "winning_custom_audience_ad_counter_int_keys AS ad_counter_int_keys "
433                     + "FROM ad_selection_result results "
434                     + "JOIN ad_selection_initialization init "
435                     + "ON results.ad_selection_id = init.ad_selection_id "
436                     + "WHERE init.ad_selection_id = :adSelectionId "
437                     + "AND init.caller_package_name = :callerPackageName")
438     @Nullable
getAdSelectionHistogramInfoFromUnifiedTable( long adSelectionId, @NonNull String callerPackageName)439     public abstract DBAdSelectionHistogramInfo getAdSelectionHistogramInfoFromUnifiedTable(
440             long adSelectionId, @NonNull String callerPackageName);
441 
442     /**
443      * Clean up expired adSelection entries if it is older than the given timestamp. If
444      * creation_timestamp < expirationTime, the ad selection entry will be removed from the
445      * ad_selection table.
446      *
447      * @param expirationTime is the cutoff time to expire the AdSelectionEntry.
448      */
449     @Query("DELETE FROM ad_selection WHERE creation_timestamp < :expirationTime")
removeExpiredAdSelection(Instant expirationTime)450     public abstract void removeExpiredAdSelection(Instant expirationTime);
451 
452     /**
453      * Clean up expired ad selection initialization entries if it is older than the given timestamp.
454      * If creation_instant < expirationTime, the ad selection initialization will be removed from
455      * the ad_selection_initialization table. It will also remove the entries from the other table
456      * with ad_selection_id as the foreign key because onDelete cascade is set.
457      *
458      * @param expirationTime is the cutoff time to expire the AdSelectionEntry.
459      */
460     @Query("DELETE FROM ad_selection_initialization WHERE creation_instant < :expirationTime")
removeExpiredAdSelectionInitializations(Instant expirationTime)461     public abstract void removeExpiredAdSelectionInitializations(Instant expirationTime);
462 
463     /**
464      * Clean up selected ad selection data entry data in batch by their ad_selection_ids.
465      *
466      * @param adSelectionIds is the list of adSelectionIds to identify the data entries to be
467      *     removed from ad_selection and buyer_decision_logic tables.
468      */
469     @Query("DELETE FROM ad_selection WHERE ad_selection_id IN (:adSelectionIds)")
removeAdSelectionEntriesByIds(List<Long> adSelectionIds)470     public abstract void removeAdSelectionEntriesByIds(List<Long> adSelectionIds);
471 
472     /**
473      * Clean up selected ad selection override data by its {@code adSelectionConfigId}
474      *
475      * @param adSelectionConfigId is the {@code adSelectionConfigId} to identify the data entries to
476      *     be removed from the ad_selection_overrides table.
477      */
478     @Query(
479             "DELETE FROM ad_selection_overrides WHERE ad_selection_config_id = :adSelectionConfigId"
480                     + " AND app_package_name = :appPackageName")
removeAdSelectionOverrideByIdAndPackageName( String adSelectionConfigId, String appPackageName)481     public abstract void removeAdSelectionOverrideByIdAndPackageName(
482             String adSelectionConfigId, String appPackageName);
483 
484     /**
485      * Clean up buyer decision logic override data by its {@code adSelectionConfigId}
486      *
487      * @param adSelectionConfigId is the {@code adSelectionConfigId} to identify the data entries to
488      *     be removed from the ad_selection_overrides table.
489      */
490     @Query(
491             "DELETE FROM ad_selection_buyer_logic_overrides WHERE ad_selection_config_id = "
492                     + ":adSelectionConfigId AND app_package_name = :appPackageName")
removeBuyerDecisionLogicOverrideByIdAndPackageName( String adSelectionConfigId, String appPackageName)493     public abstract void removeBuyerDecisionLogicOverrideByIdAndPackageName(
494             String adSelectionConfigId, String appPackageName);
495 
496     /**
497      * Clean up buyer_decision_logic entries in batch if the bidding_logic_uri no longer exists in
498      * the table ad_selection.
499      */
500     @Query(
501             "DELETE FROM buyer_decision_logic WHERE bidding_logic_uri NOT IN "
502                     + "( SELECT DISTINCT bidding_logic_uri "
503                     + "FROM ad_selection "
504                     + "WHERE bidding_logic_uri is NOT NULL)")
removeExpiredBuyerDecisionLogic()505     public abstract void removeExpiredBuyerDecisionLogic();
506 
507     /** Clean up all ad selection override data */
508     @Query("DELETE FROM ad_selection_overrides WHERE  app_package_name = :appPackageName")
removeAllAdSelectionOverrides(String appPackageName)509     public abstract void removeAllAdSelectionOverrides(String appPackageName);
510 
511     /** Clean up all buyers' decision logic data */
512     @Query(
513             "DELETE FROM ad_selection_buyer_logic_overrides WHERE  app_package_name ="
514                     + " :appPackageName")
removeAllBuyerDecisionOverrides(String appPackageName)515     public abstract void removeAllBuyerDecisionOverrides(String appPackageName);
516 
517     /**
518      * Checks if there is a row in the ad selection data with the unique combination of
519      * ad_selection_id and caller_package_name
520      *
521      * @param adSelectionId which is the key to query the corresponding ad selection data.
522      * @param callerPackageName the caller's package name, to be verified against the
523      *     calling_package_name that exists in the ad_selection_entry
524      * @return true if row exists, false otherwise
525      */
526     @Query(
527             "SELECT EXISTS(SELECT 1 FROM ad_selection WHERE ad_selection_id = :adSelectionId"
528                     + " AND caller_package_name = :callerPackageName LIMIT"
529                     + " 1)")
doesAdSelectionMatchingCallerPackageNameExistInOnDeviceTable( long adSelectionId, String callerPackageName)530     public abstract boolean doesAdSelectionMatchingCallerPackageNameExistInOnDeviceTable(
531             long adSelectionId, String callerPackageName);
532 
533     /**
534      * Checks if there is a row in the ad selection initizalization table with the unique
535      * combination of ad_selection_id and caller_package_name
536      *
537      * @param adSelectionId which is the key to query the corresponding ad selection data.
538      * @param callerPackageName the caller's package name, to be verified against the
539      *     calling_package_name that exists in the ad_selection_entry
540      * @return true if row exists, false otherwise
541      */
542     @Query(
543             "SELECT EXISTS(SELECT 1 FROM ad_selection_initialization WHERE ad_selection_id ="
544                     + " :adSelectionId AND caller_package_name = :callerPackageName LIMIT 1)")
doesAdSelectionMatchingCallerPackageNameExistInServerAuctionTable( long adSelectionId, String callerPackageName)545     public abstract boolean doesAdSelectionMatchingCallerPackageNameExistInServerAuctionTable(
546             long adSelectionId, String callerPackageName);
547 
548     /**
549      * Checks if there is a row in either of the ad selection tables with the unique combination of
550      * ad_selection_id and caller_package_name
551      */
552     @Transaction
doesAdSelectionIdAndCallerPackageNameExists( long adSelectionId, String callerPackageName)553     public boolean doesAdSelectionIdAndCallerPackageNameExists(
554             long adSelectionId, String callerPackageName) {
555         return doesAdSelectionMatchingCallerPackageNameExistInOnDeviceTable(
556                         adSelectionId, callerPackageName)
557                 || doesAdSelectionMatchingCallerPackageNameExistInServerAuctionTable(
558                         adSelectionId, callerPackageName);
559     }
560 
561     /**
562      * Add an ad selection from outcomes override into the table
563      * ad_selection_from_outcomes_overrides
564      *
565      * @param adSelectionFromOutcomesOverride is the AdSelectionFromOutcomesOverride to add to table
566      *     ad_selection_overrides. If a {@link DBAdSelectionFromOutcomesOverride} object with the
567      *     {@code adSelectionConfigFromOutcomesId} already exists, this will replace the existing
568      *     object.
569      */
570     @Insert(onConflict = OnConflictStrategy.REPLACE)
persistAdSelectionFromOutcomesOverride( DBAdSelectionFromOutcomesOverride adSelectionFromOutcomesOverride)571     public abstract void persistAdSelectionFromOutcomesOverride(
572             DBAdSelectionFromOutcomesOverride adSelectionFromOutcomesOverride);
573 
574     /**
575      * Checks if there is a row in the ad selection override data with the unique key
576      * ad_selection_from_outcomes_config_id
577      *
578      * @param adSelectionFromOutcomesConfigId which is the key to query the corresponding ad
579      *     selection override data.
580      * @return true if row exists, false otherwise
581      */
582     @Query(
583             "SELECT EXISTS(SELECT 1 FROM ad_selection_from_outcomes_overrides WHERE "
584                     + "ad_selection_from_outcomes_config_id = "
585                     + ":adSelectionFromOutcomesConfigId AND app_package_name = :appPackageName "
586                     + "LIMIT 1)")
doesAdSelectionFromOutcomesOverrideExistForPackageName( String adSelectionFromOutcomesConfigId, String appPackageName)587     public abstract boolean doesAdSelectionFromOutcomesOverrideExistForPackageName(
588             String adSelectionFromOutcomesConfigId, String appPackageName);
589 
590     /**
591      * Get ad selection from outcomes selection logic JS override by its unique key and the package
592      * name of the app that created the override.
593      *
594      * @return ad selection override result if exists.
595      */
596     @Query(
597             "SELECT selection_logic_js FROM ad_selection_from_outcomes_overrides WHERE "
598                     + "ad_selection_from_outcomes_config_id = :adSelectionFromOutcomesConfigId "
599                     + "AND app_package_name = :appPackageName")
600     @Nullable
getSelectionLogicOverride( String adSelectionFromOutcomesConfigId, String appPackageName)601     public abstract String getSelectionLogicOverride(
602             String adSelectionFromOutcomesConfigId, String appPackageName);
603 
604     /**
605      * Get ad selection from outcomes signals override by its unique key and the package name of the
606      * app that created the override.
607      *
608      * @return ad selection from outcomes override result if exists.
609      */
610     @Query(
611             "SELECT selection_signals FROM ad_selection_from_outcomes_overrides WHERE"
612                     + " ad_selection_from_outcomes_config_id = :adSelectionFromOutcomesConfigId "
613                     + "AND app_package_name = :appPackageName")
614     @Nullable
getSelectionSignalsOverride( String adSelectionFromOutcomesConfigId, String appPackageName)615     public abstract String getSelectionSignalsOverride(
616             String adSelectionFromOutcomesConfigId, String appPackageName);
617 
618     /**
619      * Clean up selected ad selection from outcomes override data by its {@code
620      * adSelectionFromOutcomesConfigId}
621      *
622      * @param adSelectionFromOutcomesConfigId is to identify the data entries to be removed from the
623      *     ad_selection_overrides table.
624      */
625     @Query(
626             "DELETE FROM ad_selection_from_outcomes_overrides WHERE "
627                     + "ad_selection_from_outcomes_config_id = :adSelectionFromOutcomesConfigId AND "
628                     + "app_package_name = :appPackageName")
removeAdSelectionFromOutcomesOverrideByIdAndPackageName( String adSelectionFromOutcomesConfigId, String appPackageName)629     public abstract void removeAdSelectionFromOutcomesOverrideByIdAndPackageName(
630             String adSelectionFromOutcomesConfigId, String appPackageName);
631 
632     /** Clean up all ad selection from outcomes override data */
633     @Query(
634             "DELETE FROM ad_selection_from_outcomes_overrides WHERE app_package_name = "
635                     + ":appPackageName")
removeAllAdSelectionFromOutcomesOverrides(String appPackageName)636     public abstract void removeAllAdSelectionFromOutcomesOverrides(String appPackageName);
637 
638     /**
639      * Clean up registered_ad_interaction entries in batch if the {@code adSelectionId} no longer
640      * exists in the table ad_selection.
641      */
642     @Query(
643             "DELETE FROM registered_ad_interactions WHERE ad_selection_id NOT IN "
644                     + "( SELECT DISTINCT ad_selection_id "
645                     + "FROM ad_selection "
646                     + "WHERE ad_selection_id is NOT NULL)")
removeExpiredRegisteredAdInteractions()647     public abstract void removeExpiredRegisteredAdInteractions();
648 
649     /**
650      * Clean up registered_ad_interaction entries in batch if the {@code adSelectionId} no longer
651      * exists in the table ad_selection_initialization.
652      */
653     @Query(
654             "DELETE FROM registered_ad_interactions WHERE ad_selection_id NOT IN "
655                     + "( SELECT DISTINCT ad_selection_id "
656                     + "FROM ad_selection_initialization "
657                     + "WHERE ad_selection_id is NOT NULL)")
removeExpiredRegisteredAdInteractionsFromUnifiedTable()658     public abstract void removeExpiredRegisteredAdInteractionsFromUnifiedTable();
659 
660     /** Returns total size of the {@code registered_ad_interaction} table. */
661     @Query("SELECT COUNT(*) FROM registered_ad_interactions")
getTotalNumRegisteredAdInteractions()662     public abstract long getTotalNumRegisteredAdInteractions();
663 
664     /**
665      * Returns total number of the {@code registered_ad_interaction}s that match a given {@code
666      * adSelectionId} and {@code reportingDestination}.
667      */
668     @Query(
669             "SELECT COUNT(*) FROM registered_ad_interactions WHERE ad_selection_id ="
670                     + " :adSelectionId AND destination = :reportingDestination")
getNumRegisteredAdInteractionsPerAdSelectionAndDestination( long adSelectionId, @ReportEventRequest.ReportingDestination int reportingDestination)671     public abstract long getNumRegisteredAdInteractionsPerAdSelectionAndDestination(
672             long adSelectionId, @ReportEventRequest.ReportingDestination int reportingDestination);
673 
674     /**
675      * Inserts a list of {@link DBRegisteredAdInteraction}s into the database, enforcing these
676      * limitations:
677      *
678      * <p>We will not allow the total size of the {@code registered_ad_interaction} to exceed {@code
679      * maxTotalNumRegisteredInteractions}
680      *
681      * <p>We will not allow the number of registered ad interactions {@code adSelectionId} and
682      * {@code reportingDestination} to exceed {@code maxPerDestinationNumRegisteredInteractions}.
683      *
684      * <p>This transaction is separate in order to minimize the critical region while locking the
685      * database.
686      */
687     @Transaction
safelyInsertRegisteredAdInteractions( long adSelectionId, @NonNull List<DBRegisteredAdInteraction> registeredAdInteractions, long maxTotalNumRegisteredInteractions, long maxPerDestinationNumRegisteredInteractions, int reportingDestination)688     public void safelyInsertRegisteredAdInteractions(
689             long adSelectionId,
690             @NonNull List<DBRegisteredAdInteraction> registeredAdInteractions,
691             long maxTotalNumRegisteredInteractions,
692             long maxPerDestinationNumRegisteredInteractions,
693             int reportingDestination) {
694         long currentNumRegisteredInteractions = getTotalNumRegisteredAdInteractions();
695 
696         if (currentNumRegisteredInteractions >= maxTotalNumRegisteredInteractions) {
697             sLogger.v("Registered Ad Interaction max table size reached! Skipping entire list.");
698             return;
699         }
700 
701         long currentNumRegisteredInteractionsPerDestination =
702                 getNumRegisteredAdInteractionsPerAdSelectionAndDestination(
703                         adSelectionId, reportingDestination);
704 
705         if (currentNumRegisteredInteractionsPerDestination
706                 >= maxPerDestinationNumRegisteredInteractions) {
707             sLogger.v(
708                     "Maximum number of Registered Ad Interactions for this adSelectionId and"
709                             + " reportingDestination reached! Skipping entire list.");
710             return;
711         }
712 
713         long numAvailableRowsInTable =
714                 Math.max(0, maxTotalNumRegisteredInteractions - currentNumRegisteredInteractions);
715 
716         long numAvailableRowsInTablePerAdSelectionIdAndDestination =
717                 Math.max(
718                         0,
719                         maxPerDestinationNumRegisteredInteractions
720                                 - currentNumRegisteredInteractionsPerDestination);
721 
722         int numEntriesToCommit =
723                 (int)
724                         Math.min(
725                                 numAvailableRowsInTablePerAdSelectionIdAndDestination,
726                                 Math.min(registeredAdInteractions.size(), numAvailableRowsInTable));
727         List<DBRegisteredAdInteraction> registeredAdInteractionsToCommit =
728                 registeredAdInteractions.subList(0, numEntriesToCommit);
729 
730         persistDBRegisteredAdInteractions(registeredAdInteractionsToCommit);
731     }
732 
733     /** Checks if adSelectionId exists in {@link DBAdSelectionInitialization}. */
734     @Query(
735             "SELECT EXISTS(SELECT 1 FROM ad_selection_initialization "
736                     + "WHERE ad_selection_id = :adSelectionId)")
doesAdSelectionIdExistInInitializationTable(long adSelectionId)737     public abstract boolean doesAdSelectionIdExistInInitializationTable(long adSelectionId);
738 
739     /**
740      * Checks if adSelectionId exists in {@link DBAdSelectionInitialization} or in {@link
741      * DBAdSelection}, depending on the flag.
742      */
743     @Transaction
doesAdSelectionIdExistUponFlag( long adSelectionId, boolean shouldCheckUnifiedTable)744     public boolean doesAdSelectionIdExistUponFlag(
745             long adSelectionId, boolean shouldCheckUnifiedTable) {
746         if (shouldCheckUnifiedTable) {
747             return doesAdSelectionIdExistInInitializationTable(adSelectionId);
748         }
749         return doesAdSelectionIdExist(adSelectionId);
750     }
751 
752     /**
753      * Checks if there is a row in the ad selection with the unique key ad_selection_id and caller
754      * package name.
755      *
756      * @param adSelectionIds which is the key to query the corresponding ad selection data.
757      * @param callerPackageName package name which initiated the auction run
758      * @return true if row exists, false otherwise
759      */
760     @Query(
761             "SELECT ad_selection_id FROM ad_selection WHERE ad_selection_id IN (:adSelectionIds)"
762                     + " AND caller_package_name = :callerPackageName")
getAdSelectionIdsWithCallerPackageNameInOnDeviceTable( List<Long> adSelectionIds, String callerPackageName)763     public abstract List<Long> getAdSelectionIdsWithCallerPackageNameInOnDeviceTable(
764             List<Long> adSelectionIds, String callerPackageName);
765 
766     /**
767      * Checks if there is a row in the ad selection and ad_selection_initialization with the unique
768      * key ad_selection_id and caller package name.
769      *
770      * @param adSelectionIds which is the key to query the corresponding ad selection data.
771      * @param callerPackageName package name which initiated the auction run
772      * @return true if row exists, false otherwise
773      */
774     @Query(
775             "SELECT ad_selection_id FROM ad_selection WHERE"
776                     + " ad_selection_id IN (:adSelectionIds)"
777                     + " AND caller_package_name = :callerPackageName "
778                     + " UNION"
779                     + " SELECT ad_selection_id FROM ad_selection_initialization "
780                     + " WHERE ad_selection_id IN (:adSelectionIds) "
781                     + " AND caller_package_name = :callerPackageName ")
getAdSelectionIdsWithCallerPackageName( List<Long> adSelectionIds, String callerPackageName)782     public abstract List<Long> getAdSelectionIdsWithCallerPackageName(
783             List<Long> adSelectionIds, String callerPackageName);
784 
785     /**
786      * Checks if there is a row in the ad_selection_initialization table with the unique key
787      * ad_selection_id and caller package name.
788      *
789      * @param adSelectionIds which is the key to query the corresponding ad selection data.
790      * @param callerPackageName package name which initiated the auction run
791      * @return true if row exists, false otherwise
792      */
793     @Query(
794             " SELECT ad_selection_id FROM ad_selection_initialization "
795                     + " WHERE ad_selection_id IN (:adSelectionIds) "
796                     + " AND caller_package_name = :callerPackageName ")
getAdSelectionIdsWithCallerPackageNameFromUnifiedTable( List<Long> adSelectionIds, String callerPackageName)797     public abstract List<Long> getAdSelectionIdsWithCallerPackageNameFromUnifiedTable(
798             List<Long> adSelectionIds, String callerPackageName);
799 
800     /**
801      * Method used to create an AdSelectionId record in ad selection initialization.
802      *
803      * @return true if row was created in DBAdSelectionInitialization, false otherwise
804      */
805     @Transaction
persistAdSelectionInitialization( long adSelectionId, AdSelectionInitialization adSelectionInitialization)806     public boolean persistAdSelectionInitialization(
807             long adSelectionId, AdSelectionInitialization adSelectionInitialization) {
808         if (doesAdSelectionIdExistInInitializationTable(adSelectionId)
809                 || doesAdSelectionIdExist(adSelectionId)) {
810             return false;
811         }
812         DBAdSelectionInitialization dbAdSelectionInitialization =
813                 DBAdSelectionInitialization.builder()
814                         .setAdSelectionId(adSelectionId)
815                         .setCallerPackageName(adSelectionInitialization.getCallerPackageName())
816                         .setSeller(adSelectionInitialization.getSeller())
817                         .setCreationInstant(adSelectionInitialization.getCreationInstant())
818                         .build();
819         insertDBAdSelectionInitialization(dbAdSelectionInitialization);
820         return true;
821     }
822 
823     /** Inserts AdSelectionResult data to {@link DBAdSelectionResult}. */
persistAdSelectionResultForCustomAudience( long adSelectionId, AdSelectionResultBidAndUri adSelectionResult, AdTechIdentifier winningAdBuyer, WinningCustomAudience winningCustomAudience)824     public void persistAdSelectionResultForCustomAudience(
825             long adSelectionId,
826             AdSelectionResultBidAndUri adSelectionResult,
827             AdTechIdentifier winningAdBuyer,
828             WinningCustomAudience winningCustomAudience) {
829         DBWinningCustomAudience dbWinningCustomAudience =
830                 DBWinningCustomAudience.builder()
831                         .setName(winningCustomAudience.getName())
832                         .setOwner(winningCustomAudience.getOwner())
833                         .setAdCounterIntKeys(winningCustomAudience.getAdCounterKeys())
834                         .build();
835 
836         DBAdSelectionResult dbAdSelectionResult =
837                 DBAdSelectionResult.builder()
838                         .setAdSelectionId(adSelectionId)
839                         .setWinningAdBid(adSelectionResult.getWinningAdBid())
840                         .setWinningAdRenderUri(adSelectionResult.getWinningAdRenderUri())
841                         .setWinningBuyer(winningAdBuyer)
842                         .setWinningCustomAudience(dbWinningCustomAudience)
843                         .setComponentAdRenderUris(adSelectionResult.getComponentAdRenderUris())
844                         .build();
845 
846         insertDBAdSelectionResult(dbAdSelectionResult);
847     }
848 
849     /**
850      * Inserts the reporting data to DBReportingData corresponding to the ad selection run with
851      * adselectionId.
852      */
persistReportingData(long adSelectionId, ReportingData reportingData)853     public void persistReportingData(long adSelectionId, ReportingData reportingData) {
854         DBValidator.validateReportingData(reportingData);
855 
856         DBReportingData dbReportingData =
857                 DBReportingData.builder()
858                         .setAdSelectionId(adSelectionId)
859                         .setBuyerReportingUri(reportingData.getBuyerWinReportingUri())
860                         .setSellerReportingUri(reportingData.getSellerWinReportingUri())
861                         .setComponentSellerReportingUri(
862                                 reportingData.getComponentSellerWinReportingUri())
863                         .build();
864 
865         insertDBReportingData(dbReportingData);
866     }
867 
868     /** Reads ReportingData from DB associated with the given adSelectionId. */
getReportingDataForId(long adSelectionId, boolean shouldUseUnifiedTables)869     public ReportingData getReportingDataForId(long adSelectionId, boolean shouldUseUnifiedTables) {
870         if (doesAdSelectionIdExistInInitializationTable(adSelectionId)) {
871             ReportingData uris = getReportingUris(adSelectionId);
872             if (!Objects.isNull(uris)) {
873                 // Only return if Uris are found, otherwise try to compute if unified flag is on
874                 return uris;
875             } else if (shouldUseUnifiedTables) {
876                 DBReportingComputationInfo reportingComputationInfoById =
877                         getReportingComputationInfoById(adSelectionId);
878                 ReportingComputationData reportingComputationData =
879                         ReportingComputationData.builder()
880                                 .setBuyerDecisionLogicJs(
881                                         reportingComputationInfoById.getBuyerDecisionLogicJs())
882                                 .setBuyerDecisionLogicUri(
883                                         reportingComputationInfoById.getBiddingLogicUri())
884                                 .setSellerContextualSignals(
885                                         parseAdSelectionSignalsOrEmpty(
886                                                 reportingComputationInfoById
887                                                         .getSellerContextualSignals()))
888                                 .setBuyerContextualSignals(
889                                         parseAdSelectionSignalsOrEmpty(
890                                                 reportingComputationInfoById
891                                                         .getBuyerContextualSignals()))
892                                 .setWinningCustomAudienceSignals(
893                                         reportingComputationInfoById.getCustomAudienceSignals())
894                                 .setWinningRenderUri(
895                                         reportingComputationInfoById.getWinningAdRenderUri())
896                                 .setWinningBid(reportingComputationInfoById.getWinningAdBid())
897                                 .build();
898                 return ReportingData.builder()
899                         .setReportingComputationData(reportingComputationData)
900                         .build();
901             }
902         } else if (!shouldUseUnifiedTables && doesAdSelectionIdExist(adSelectionId)) {
903             // only look in old tables if unified tables flag is off
904             DBAdSelectionEntry adSelectionEntry = getAdSelectionEntityById(adSelectionId);
905             ReportingComputationData reportingComputationData =
906                     ReportingComputationData.builder()
907                             .setBuyerDecisionLogicJs(adSelectionEntry.getBuyerDecisionLogicJs())
908                             .setBuyerDecisionLogicUri(adSelectionEntry.getBiddingLogicUri())
909                             .setSellerContextualSignals(
910                                     parseAdSelectionSignalsOrEmpty(
911                                             adSelectionEntry.getSellerContextualSignals()))
912                             .setBuyerContextualSignals(
913                                     parseAdSelectionSignalsOrEmpty(
914                                             adSelectionEntry.getBuyerContextualSignals()))
915                             .setWinningCustomAudienceSignals(
916                                     adSelectionEntry.getCustomAudienceSignals())
917                             .setWinningRenderUri(adSelectionEntry.getWinningAdRenderUri())
918                             .setWinningBid(adSelectionEntry.getWinningAdBid())
919                             .build();
920             return ReportingData.builder()
921                     .setReportingComputationData(reportingComputationData)
922                     .build();
923         }
924         // no reporting Info for this ad selection id
925         return null;
926     }
927 
928     /**
929      * Inserts a list of interaction Uri and interaction keys into the database, enforcing these
930      * limitations:
931      *
932      * <p>We will not allow the total size of the {@code registered_ad_interaction} to exceed {@code
933      * maxTotalNumRegisteredInteractions}
934      *
935      * <p>We will not allow the number of registered ad interactions {@code adSelectionId} and
936      * {@code reportingDestination} to exceed {@code maxPerDestinationNumRegisteredInteractions}.
937      */
safelyInsertRegisteredAdInteractionsForDestination( long adSelectionId, @ReportEventRequest.ReportingDestination int reportingDestination, List<RegisteredAdInteraction> adInteractions, long maxTotalNumRegisteredInteractions, long maxPerDestinationNumRegisteredInteractions)938     public void safelyInsertRegisteredAdInteractionsForDestination(
939             long adSelectionId,
940             @ReportEventRequest.ReportingDestination int reportingDestination,
941             List<RegisteredAdInteraction> adInteractions,
942             long maxTotalNumRegisteredInteractions,
943             long maxPerDestinationNumRegisteredInteractions) {
944         List<DBRegisteredAdInteraction> interactions =
945                 adInteractions.stream()
946                         .map(
947                                 adInteraction ->
948                                         DBRegisteredAdInteraction.builder()
949                                                 .setAdSelectionId(adSelectionId)
950                                                 .setDestination(reportingDestination)
951                                                 .setInteractionKey(
952                                                         adInteraction.getInteractionKey())
953                                                 .setInteractionReportingUri(
954                                                         adInteraction.getInteractionReportingUri())
955                                                 .build())
956                         .collect(Collectors.toList());
957 
958         safelyInsertRegisteredAdInteractions(
959                 adSelectionId,
960                 interactions,
961                 maxTotalNumRegisteredInteractions,
962                 maxPerDestinationNumRegisteredInteractions,
963                 reportingDestination);
964     }
965 
966     /** Query reporting URI records from DBReportingData if adSelectionId exists. */
967     @Query(
968             "SELECT buyer_reporting_uri AS buyerWinReportingUri, "
969                     + "seller_reporting_uri AS sellerWinReportingUri, "
970                     + "component_seller_reporting_uri AS componentSellerWinReportingUri "
971                     + "FROM reporting_data WHERE ad_selection_id = :adSelectionId")
getReportingUris(long adSelectionId)972     public abstract ReportingData getReportingUris(long adSelectionId);
973 
974     /** Query to fetch caller package name and seller which initialized the ad selection run. */
975     @Query(
976             "SELECT seller, "
977                     + "caller_package_name AS callerPackageName, "
978                     + "creation_instant AS creationInstant "
979                     + "FROM ad_selection_initialization WHERE ad_selection_id = :adSelectionId")
getAdSelectionInitializationForId(long adSelectionId)980     public abstract AdSelectionInitialization getAdSelectionInitializationForId(long adSelectionId);
981 
982     /** Query to fetch winning buyer of ad selection run identified by adSelectionId. */
983     @Query("SELECT winning_buyer FROM ad_selection_result WHERE ad_selection_id = :adSelectionId")
getWinningBuyerForId(long adSelectionId)984     public abstract AdTechIdentifier getWinningBuyerForId(long adSelectionId);
985 
986     /** Query winning custom audience data of an ad selection run identified by adSelectionId. */
987     @Query(
988             "SELECT winning_custom_audience_name AS name ,"
989                     + "winning_custom_audience_owner AS owner, "
990                     + "winning_custom_audience_ad_counter_int_keys AS adCounterKeys "
991                     + "FROM ad_selection_result "
992                     + "WHERE ad_selection_id = :adSelectionId")
getWinningCustomAudienceDataForId(long adSelectionId)993     public abstract WinningCustomAudience getWinningCustomAudienceDataForId(long adSelectionId);
994 
995     /** Query to get winning ad data of ad selection run identified by adSelectionId. */
996     @Query(
997             "SELECT ad_selection_id AS adSelectionId, winning_ad_bid AS winningAdBid, "
998                     + "winning_ad_render_uri AS winningAdRenderUri, "
999                     + "component_ad_render_uris AS componentAdRenderUris "
1000                     + "FROM ad_selection_result "
1001                     + "WHERE ad_selection_id = :adSelectionId")
getWinningBidAndUriForId(long adSelectionId)1002     public abstract AdSelectionResultBidAndUri getWinningBidAndUriForId(long adSelectionId);
1003 
1004     /** Query to get winning ad data of ad selection run identified by adSelectionId. */
1005     @Query(
1006             "SELECT ad_selection_id AS adSelectionId, "
1007                     + "winning_ad_bid AS winningAdBid, "
1008                     + "winning_ad_render_uri AS winningAdRenderUri, "
1009                     + "component_ad_render_uris AS componentAdRenderUris "
1010                     + "FROM ad_selection_result WHERE ad_selection_id IN (:adSelectionIds) "
1011                     + "UNION "
1012                     + "SELECT ad_selection_id AS adSelectionId, "
1013                     + "winning_ad_bid AS winningAdBid, "
1014                     + "winning_ad_render_uri AS winningAdRenderUri, "
1015                     + " '' AS componentAdRenderUris "
1016                     + "FROM ad_selection WHERE ad_selection_id IN (:adSelectionIds)")
getWinningBidAndUriForIds( List<Long> adSelectionIds)1017     public abstract List<AdSelectionResultBidAndUri> getWinningBidAndUriForIds(
1018             List<Long> adSelectionIds);
1019 
1020     /**
1021      * Query the unified table to get winning ad data of ad selection run identified by
1022      * adSelectionId.
1023      */
1024     @Query(
1025             "SELECT ad_selection_id AS adSelectionId, "
1026                     + "winning_ad_bid AS winningAdBid, "
1027                     + "winning_ad_render_uri AS winningAdRenderUri, "
1028                     + "component_ad_render_uris AS componentAdRenderUris "
1029                     + "FROM ad_selection_result WHERE ad_selection_id IN (:adSelectionIds)")
getWinningBidAndUriForIdsUnifiedTables( List<Long> adSelectionIds)1030     public abstract List<AdSelectionResultBidAndUri> getWinningBidAndUriForIdsUnifiedTables(
1031             List<Long> adSelectionIds);
1032 
1033     /**
1034      * Query the unified table to get winning buyer of ad selection run identified by adSelectionId.
1035      */
1036     @Query("SELECT winning_buyer FROM ad_selection_result WHERE ad_selection_id = :adSelectionId")
getWinningBuyerForIdUnifiedTables(Long adSelectionId)1037     public abstract AdTechIdentifier getWinningBuyerForIdUnifiedTables(Long adSelectionId);
1038 
1039     /**
1040      * Insert new ad selection initialization record. Aborts if adselectionId already exists.
1041      *
1042      * @param dbAdSelectionInitialization the record keyed by adSelectionId to insert.
1043      */
1044     @Insert(onConflict = OnConflictStrategy.ABORT)
insertDBAdSelectionInitialization( DBAdSelectionInitialization dbAdSelectionInitialization)1045     abstract void insertDBAdSelectionInitialization(
1046             DBAdSelectionInitialization dbAdSelectionInitialization);
1047 
1048     /**
1049      * Insert new ad selection result record. Aborts if adselectionId already exists.
1050      *
1051      * @param dbAdSelectionResult the record keyed by adSelectionId to insert.
1052      */
1053     @Insert(onConflict = OnConflictStrategy.ABORT)
insertDBAdSelectionResult(DBAdSelectionResult dbAdSelectionResult)1054     abstract void insertDBAdSelectionResult(DBAdSelectionResult dbAdSelectionResult);
1055 
1056     /** Insert new {@link DBReportingComputationInfo} record. */
1057     @Insert(onConflict = OnConflictStrategy.ABORT)
insertDBReportingComputationInfo( DBReportingComputationInfo dbAdSelectionResult)1058     public abstract void insertDBReportingComputationInfo(
1059             DBReportingComputationInfo dbAdSelectionResult);
1060 
1061     /**
1062      * Insert a reporting URI record. Aborts if adselectionId already exists.
1063      *
1064      * @param dbReportingData the record keyed by adSelectionId to insert.
1065      */
1066     @Insert(onConflict = OnConflictStrategy.ABORT)
insertDBReportingData(DBReportingData dbReportingData)1067     abstract void insertDBReportingData(DBReportingData dbReportingData);
1068 
1069     /** Query to get DBAdSelectionInitialization for the given adSelectionId. */
1070     @Query("SELECT * FROM ad_selection_initialization WHERE ad_selection_id = :adSelectionId")
getDBAdSelectionInitializationForId(long adSelectionId)1071     abstract DBAdSelectionInitialization getDBAdSelectionInitializationForId(long adSelectionId);
1072 
1073     /** Query to get DBAdSelectionResult for the given adSelectionId. */
1074     @Query("SELECT * FROM ad_selection_result WHERE ad_selection_id = :adSelectionId")
getDBAdSelectionResultForId(long adSelectionId)1075     abstract DBAdSelectionResult getDBAdSelectionResultForId(long adSelectionId);
1076 
1077     /** Query to get DBReportingData for the given adSelectionId. */
1078     @Query("SELECT * FROM reporting_data WHERE ad_selection_id = :adSelectionId")
getDBReportingDataForId(long adSelectionId)1079     abstract DBReportingData getDBReportingDataForId(long adSelectionId);
1080 
parseAdSelectionSignalsOrEmpty(String signals)1081     private AdSelectionSignals parseAdSelectionSignalsOrEmpty(String signals) {
1082         return Objects.isNull(signals)
1083                 ? AdSelectionSignals.EMPTY
1084                 : AdSelectionSignals.fromString(signals);
1085     }
1086 }
1087