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