• 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.ReportInteractionRequest;
20 import android.net.Uri;
21 
22 import androidx.annotation.NonNull;
23 import androidx.annotation.Nullable;
24 import androidx.room.Dao;
25 import androidx.room.Insert;
26 import androidx.room.OnConflictStrategy;
27 import androidx.room.Query;
28 import androidx.room.Transaction;
29 
30 import com.android.adservices.LoggerFactory;
31 
32 import java.time.Instant;
33 import java.util.List;
34 
35 /**
36  * Data Access Object interface for access to the local AdSelection data storage.
37  *
38  * <p>Annotation will generate Room based SQLite Dao implementation.
39  */
40 @Dao
41 public abstract class AdSelectionEntryDao {
42     private static final LoggerFactory.Logger sLogger = LoggerFactory.getFledgeLogger();
43     /**
44      * Add a new successful ad selection entry into the table ad_selection.
45      *
46      * @param adSelection is the AdSelection to add to the table ad_selection if the ad_selection_id
47      *     not exists.
48      */
49     // TODO(b/230568647): retry adSelectionId generation in case of collision
50     @Insert(onConflict = OnConflictStrategy.ABORT)
persistAdSelection(DBAdSelection adSelection)51     public abstract void persistAdSelection(DBAdSelection adSelection);
52 
53     /**
54      * Write a buyer decision logic entry into the table buyer_decision_logic.
55      *
56      * @param buyerDecisionLogic is the BuyerDecisionLogic to write to table buyer_decision_logic.
57      */
58     @Insert(onConflict = OnConflictStrategy.REPLACE)
persistBuyerDecisionLogic(DBBuyerDecisionLogic buyerDecisionLogic)59     public abstract void persistBuyerDecisionLogic(DBBuyerDecisionLogic buyerDecisionLogic);
60 
61     /**
62      * Add an ad selection override into the table ad_selection_overrides
63      *
64      * @param adSelectionOverride is the AdSelectionOverride to add to table ad_selection_overrides.
65      *     If a {@link DBAdSelectionOverride} object with the {@code adSelectionConfigId} already
66      *     exists, this will replace the existing object.
67      */
68     @Insert(onConflict = OnConflictStrategy.REPLACE)
persistAdSelectionOverride(DBAdSelectionOverride adSelectionOverride)69     public abstract void persistAdSelectionOverride(DBAdSelectionOverride adSelectionOverride);
70 
71     /**
72      * Add an ad selection override for Buyers' decision logic
73      *
74      * @param buyersDecisionLogicOverride is an override for the ad_selection_buyer_logic_overrides
75      *     If a {@link DBBuyerDecisionOverride} object with the {@code adSelectionConfigId} already
76      *     exists, this will replace the existing object.
77      */
78     @Insert(onConflict = OnConflictStrategy.REPLACE)
persistBuyersDecisionLogicOverride( List<DBBuyerDecisionOverride> buyersDecisionLogicOverride)79     public abstract void persistBuyersDecisionLogicOverride(
80             List<DBBuyerDecisionOverride> buyersDecisionLogicOverride);
81 
82     /**
83      * Adds a list of registered ad interactions to the table registered_ad_interactions
84      *
85      * <p>This method is not meant to be used on its own, since it doesn't take into account the
86      * maximum size of {@code registered_ad_interactions}. Use {@link
87      * #safelyInsertRegisteredAdInteractions(long, List, long, long, int)} instead.
88      *
89      * @param registeredAdInteractions is the list of {@link DBRegisteredAdInteraction} objects to
90      *     write to the table registered_ad_interactions.
91      */
92     @Insert(onConflict = OnConflictStrategy.REPLACE)
persistDBRegisteredAdInteractions( List<DBRegisteredAdInteraction> registeredAdInteractions)93     protected abstract void persistDBRegisteredAdInteractions(
94             List<DBRegisteredAdInteraction> registeredAdInteractions);
95 
96     /**
97      * Checks if there is a row in the ad selection data with the unique key ad_selection_id
98      *
99      * @param adSelectionId which is the key to query the corresponding ad selection data.
100      * @return true if row exists, false otherwise
101      */
102     @Query(
103             "SELECT EXISTS(SELECT 1 FROM ad_selection WHERE ad_selection_id = :adSelectionId LIMIT"
104                     + " 1)")
doesAdSelectionIdExist(long adSelectionId)105     public abstract boolean doesAdSelectionIdExist(long adSelectionId);
106 
107     /**
108      * Checks if there is a row in the buyer decision logic data with the unique key
109      * bidding_logic_uri
110      *
111      * @param biddingLogicUri which is the key to query the corresponding buyer decision logic data.
112      * @return true if row exists, false otherwise
113      */
114     @Query(
115             "SELECT EXISTS(SELECT 1 FROM buyer_decision_logic WHERE bidding_logic_uri ="
116                     + " :biddingLogicUri LIMIT 1)")
doesBuyerDecisionLogicExist(Uri biddingLogicUri)117     public abstract boolean doesBuyerDecisionLogicExist(Uri biddingLogicUri);
118 
119     /**
120      * Checks if there is a row in the ad selection override data with the unique key
121      * ad_selection_config_id
122      *
123      * @param adSelectionConfigId which is the key to query the corresponding ad selection override
124      *     data.
125      * @return true if row exists, false otherwise
126      */
127     @Query(
128             "SELECT EXISTS(SELECT 1 FROM ad_selection_overrides WHERE ad_selection_config_id ="
129                     + " :adSelectionConfigId AND app_package_name = :appPackageName LIMIT 1)")
doesAdSelectionOverrideExistForPackageName( String adSelectionConfigId, String appPackageName)130     public abstract boolean doesAdSelectionOverrideExistForPackageName(
131             String adSelectionConfigId, String appPackageName);
132 
133     /**
134      * Checks if there is a row in the registered_ad_interactions table that matches the primary key
135      * combination of adSelectionId, interactionKey, and destination
136      *
137      * @param adSelectionId serves as the primary key denoting the ad selection process this entry
138      *     id associated with
139      * @param interactionKey the interaction key
140      * @param destination denotes buyer, seller, etc.
141      */
142     @Query(
143             "SELECT EXISTS(SELECT 1 FROM registered_ad_interactions WHERE ad_selection_id ="
144                     + " :adSelectionId AND interaction_key = :interactionKey AND destination"
145                     + " = :destination LIMIT 1)")
doesRegisteredAdInteractionExist( long adSelectionId, String interactionKey, @ReportInteractionRequest.ReportingDestination int destination)146     public abstract boolean doesRegisteredAdInteractionExist(
147             long adSelectionId,
148             String interactionKey,
149             @ReportInteractionRequest.ReportingDestination int destination);
150 
151     /**
152      * Get the ad selection entry by its unique key ad_selection_id.
153      *
154      * @param adSelectionId which is the key to query the corresponding ad selection entry.
155      * @return an {@link DBAdSelectionEntry} if exists.
156      */
157     @Query(
158             "SELECT ad_selection.ad_selection_id as ad_selection_id,"
159                 + " ad_selection.custom_audience_signals_owner as custom_audience_signals_owner,"
160                 + " ad_selection.custom_audience_signals_buyer as custom_audience_signals_buyer,"
161                 + " ad_selection.custom_audience_signals_name as custom_audience_signals_name,"
162                 + " ad_selection.custom_audience_signals_activation_time as"
163                 + " custom_audience_signals_activation_time,"
164                 + " ad_selection.custom_audience_signals_expiration_time as"
165                 + " custom_audience_signals_expiration_time,"
166                 + " ad_selection.custom_audience_signals_user_bidding_signals as"
167                 + " custom_audience_signals_user_bidding_signals, ad_selection.contextual_signals"
168                 + " as contextual_signals,ad_selection.winning_ad_render_uri as"
169                 + " winning_ad_render_uri,ad_selection.winning_ad_bid as"
170                 + " winning_ad_bid,ad_selection.creation_timestamp as"
171                 + " creation_timestamp,buyer_decision_logic.buyer_decision_logic_js as"
172                 + " buyer_decision_logic_js, ad_selection.bidding_logic_uri as bidding_logic_uri"
173                 + " FROM ad_selection LEFT JOIN buyer_decision_logic ON"
174                 + " ad_selection.bidding_logic_uri = buyer_decision_logic.bidding_logic_uri WHERE"
175                 + " ad_selection.ad_selection_id = :adSelectionId")
getAdSelectionEntityById(long adSelectionId)176     public abstract DBAdSelectionEntry getAdSelectionEntityById(long adSelectionId);
177 
178     /**
179      * Get the ad selection entries with a batch of ad_selection_ids.
180      *
181      * @param adSelectionIds are the list of keys to query the corresponding ad selection entries.
182      * @return ad selection entries if exists.
183      */
184     @Query(
185             "SELECT ad_selection.ad_selection_id AS"
186                 + " ad_selection_id,ad_selection.custom_audience_signals_owner as"
187                 + " custom_audience_signals_owner, ad_selection.custom_audience_signals_buyer as"
188                 + " custom_audience_signals_buyer, ad_selection.custom_audience_signals_name as"
189                 + " custom_audience_signals_name,"
190                 + " ad_selection.custom_audience_signals_activation_time as"
191                 + " custom_audience_signals_activation_time,"
192                 + " ad_selection.custom_audience_signals_expiration_time as"
193                 + " custom_audience_signals_expiration_time,"
194                 + " ad_selection.custom_audience_signals_user_bidding_signals as"
195                 + " custom_audience_signals_user_bidding_signals, ad_selection.contextual_signals"
196                 + " AS contextual_signals,ad_selection.winning_ad_render_uri AS"
197                 + " winning_ad_render_uri,ad_selection.winning_ad_bid AS winning_ad_bid,"
198                 + " ad_selection.creation_timestamp as creation_timestamp,"
199                 + " buyer_decision_logic.buyer_decision_logic_js AS buyer_decision_logic_js,"
200                 + " ad_selection.bidding_logic_uri AS bidding_logic_uri FROM ad_selection LEFT"
201                 + " JOIN buyer_decision_logic ON ad_selection.bidding_logic_uri ="
202                 + " buyer_decision_logic.bidding_logic_uri WHERE ad_selection.ad_selection_id IN"
203                 + " (:adSelectionIds) ")
getAdSelectionEntities(List<Long> adSelectionIds)204     public abstract List<DBAdSelectionEntry> getAdSelectionEntities(List<Long> adSelectionIds);
205 
206     /**
207      * Get the ad selection entries with a batch of ad_selection_ids.
208      *
209      * @param adSelectionIds are the list of keys to query the corresponding ad selection entries.
210      * @return ad selection entries if exists.
211      */
212     @Query(
213             "SELECT ad_selection.ad_selection_id AS"
214                 + " ad_selection_id,ad_selection.custom_audience_signals_owner as"
215                 + " custom_audience_signals_owner, ad_selection.custom_audience_signals_buyer as"
216                 + " custom_audience_signals_buyer, ad_selection.custom_audience_signals_name as"
217                 + " custom_audience_signals_name,"
218                 + " ad_selection.custom_audience_signals_activation_time as"
219                 + " custom_audience_signals_activation_time,"
220                 + " ad_selection.custom_audience_signals_expiration_time as"
221                 + " custom_audience_signals_expiration_time,"
222                 + " ad_selection.custom_audience_signals_user_bidding_signals as"
223                 + " custom_audience_signals_user_bidding_signals, ad_selection.contextual_signals"
224                 + " AS contextual_signals,ad_selection.winning_ad_render_uri AS"
225                 + " winning_ad_render_uri,ad_selection.winning_ad_bid AS winning_ad_bid,"
226                 + " ad_selection.creation_timestamp as creation_timestamp,"
227                 + " buyer_decision_logic.buyer_decision_logic_js AS buyer_decision_logic_js,"
228                 + " ad_selection.bidding_logic_uri AS bidding_logic_uri FROM ad_selection LEFT"
229                 + " JOIN buyer_decision_logic ON ad_selection.bidding_logic_uri ="
230                 + " buyer_decision_logic.bidding_logic_uri WHERE ad_selection.ad_selection_id IN"
231                 + " (:adSelectionIds) AND ad_selection.caller_package_name = :callerPackageName")
getAdSelectionEntities( List<Long> adSelectionIds, String callerPackageName)232     public abstract List<DBAdSelectionEntry> getAdSelectionEntities(
233             List<Long> adSelectionIds, String callerPackageName);
234 
235     /**
236      * Get ad selection JS override by its unique key and the package name of the app that created
237      * the override.
238      *
239      * @return ad selection override result if exists.
240      */
241     @Query(
242             "SELECT decision_logic FROM ad_selection_overrides WHERE ad_selection_config_id ="
243                     + " :adSelectionConfigId AND app_package_name = :appPackageName")
244     @Nullable
getDecisionLogicOverride( String adSelectionConfigId, String appPackageName)245     public abstract String getDecisionLogicOverride(
246             String adSelectionConfigId, String appPackageName);
247 
248     /**
249      * Get ad selection trusted scoring signals override by its unique key and the package name of
250      * the app that created the override.
251      *
252      * @return ad selection override result if exists.
253      */
254     @Query(
255             "SELECT trusted_scoring_signals FROM ad_selection_overrides WHERE"
256                     + " ad_selection_config_id = :adSelectionConfigId AND app_package_name ="
257                     + " :appPackageName")
258     @Nullable
getTrustedScoringSignalsOverride( String adSelectionConfigId, String appPackageName)259     public abstract String getTrustedScoringSignalsOverride(
260             String adSelectionConfigId, String appPackageName);
261 
262     /**
263      * Get ad selection buyer decision logic override by its unique key and the package name of the
264      * app that created the override.
265      *
266      * @return ad selection override result if exists.
267      */
268     @Query(
269             "SELECT * FROM ad_selection_buyer_logic_overrides WHERE"
270                     + " ad_selection_config_id = :adSelectionConfigId AND app_package_name ="
271                     + " :appPackageName")
272     @Nullable
getBuyersDecisionLogicOverride( String adSelectionConfigId, String appPackageName)273     public abstract List<DBBuyerDecisionOverride> getBuyersDecisionLogicOverride(
274             String adSelectionConfigId, String appPackageName);
275 
276     /**
277      * Gets the interaction reporting uri that was registered with the primary key combination of
278      * {@code adSelectionId}, {@code interactionKey}, and {@code destination}.
279      *
280      * @return interaction reporting uri if exists.
281      */
282     @Query(
283             "SELECT interaction_reporting_uri FROM registered_ad_interactions WHERE"
284                     + " ad_selection_id = :adSelectionId AND interaction_key = :interactionKey AND"
285                     + " destination = :destination")
286     @Nullable
getRegisteredAdInteractionUri( long adSelectionId, String interactionKey, @ReportInteractionRequest.ReportingDestination int destination)287     public abstract Uri getRegisteredAdInteractionUri(
288             long adSelectionId,
289             String interactionKey,
290             @ReportInteractionRequest.ReportingDestination int destination);
291 
292     /**
293      * Gets the {@link DBAdSelectionHistogramInfo} representing the histogram information associated
294      * with a given ad selection.
295      *
296      * @return a {@link DBAdSelectionHistogramInfo} containing the histogram info associated with
297      *     the ad selection, or {@code null} if no match is found
298      */
299     @Query(
300             "SELECT custom_audience_signals_buyer, ad_counter_keys FROM ad_selection "
301                     + "WHERE ad_selection_id = :adSelectionId "
302                     + "AND caller_package_name = :callerPackageName")
303     @Nullable
getAdSelectionHistogramInfo( long adSelectionId, @NonNull String callerPackageName)304     public abstract DBAdSelectionHistogramInfo getAdSelectionHistogramInfo(
305             long adSelectionId, @NonNull String callerPackageName);
306 
307     /**
308      * Clean up expired adSelection entries if it is older than the given timestamp. If
309      * creation_timestamp < expirationTime, the ad selection entry will be removed from the
310      * ad_selection table.
311      *
312      * @param expirationTime is the cutoff time to expire the AdSelectionEntry.
313      */
314     @Query("DELETE FROM ad_selection WHERE creation_timestamp < :expirationTime")
removeExpiredAdSelection(Instant expirationTime)315     public abstract void removeExpiredAdSelection(Instant expirationTime);
316 
317     /**
318      * Clean up selected ad selection data entry data in batch by their ad_selection_ids.
319      *
320      * @param adSelectionIds is the list of adSelectionIds to identify the data entries to be
321      *     removed from ad_selection and buyer_decision_logic tables.
322      */
323     @Query("DELETE FROM ad_selection WHERE ad_selection_id IN (:adSelectionIds)")
removeAdSelectionEntriesByIds(List<Long> adSelectionIds)324     public abstract void removeAdSelectionEntriesByIds(List<Long> adSelectionIds);
325 
326     /**
327      * Clean up selected ad selection override data by its {@code adSelectionConfigId}
328      *
329      * @param adSelectionConfigId is the {@code adSelectionConfigId} to identify the data entries to
330      *     be removed from the ad_selection_overrides table.
331      */
332     @Query(
333             "DELETE FROM ad_selection_overrides WHERE ad_selection_config_id = :adSelectionConfigId"
334                     + " AND app_package_name = :appPackageName")
removeAdSelectionOverrideByIdAndPackageName( String adSelectionConfigId, String appPackageName)335     public abstract void removeAdSelectionOverrideByIdAndPackageName(
336             String adSelectionConfigId, String appPackageName);
337 
338     /**
339      * Clean up buyer decision logic override data by its {@code adSelectionConfigId}
340      *
341      * @param adSelectionConfigId is the {@code adSelectionConfigId} to identify the data entries to
342      *     be removed from the ad_selection_overrides table.
343      */
344     @Query(
345             "DELETE FROM ad_selection_buyer_logic_overrides WHERE ad_selection_config_id = "
346                     + ":adSelectionConfigId AND app_package_name = :appPackageName")
removeBuyerDecisionLogicOverrideByIdAndPackageName( String adSelectionConfigId, String appPackageName)347     public abstract void removeBuyerDecisionLogicOverrideByIdAndPackageName(
348             String adSelectionConfigId, String appPackageName);
349 
350     /**
351      * Clean up buyer_decision_logic entries in batch if the bidding_logic_uri no longer exists in
352      * the table ad_selection.
353      */
354     @Query(
355             "DELETE FROM buyer_decision_logic WHERE bidding_logic_uri NOT IN "
356                     + "( SELECT DISTINCT bidding_logic_uri "
357                     + "FROM ad_selection "
358                     + "WHERE bidding_logic_uri is NOT NULL)")
removeExpiredBuyerDecisionLogic()359     public abstract void removeExpiredBuyerDecisionLogic();
360 
361     /** Clean up all ad selection override data */
362     @Query("DELETE FROM ad_selection_overrides WHERE  app_package_name = :appPackageName")
removeAllAdSelectionOverrides(String appPackageName)363     public abstract void removeAllAdSelectionOverrides(String appPackageName);
364 
365     /** Clean up all buyers' decision logic data */
366     @Query(
367             "DELETE FROM ad_selection_buyer_logic_overrides WHERE  app_package_name ="
368                     + " :appPackageName")
removeAllBuyerDecisionOverrides(String appPackageName)369     public abstract void removeAllBuyerDecisionOverrides(String appPackageName);
370 
371     /**
372      * Checks if there is a row in the ad selection data with the unique combination of
373      * ad_selection_id and caller_package_name
374      *
375      * @param adSelectionId which is the key to query the corresponding ad selection data.
376      * @param callerPackageName the caller's package name, to be verified against the
377      *     calling_package_name that exists in the ad_selection_entry
378      * @return true if row exists, false otherwise
379      */
380     @Query(
381             "SELECT EXISTS(SELECT 1 FROM ad_selection WHERE ad_selection_id = :adSelectionId"
382                     + " AND caller_package_name = :callerPackageName LIMIT"
383                     + " 1)")
doesAdSelectionMatchingCallerPackageNameExist( long adSelectionId, String callerPackageName)384     public abstract boolean doesAdSelectionMatchingCallerPackageNameExist(
385             long adSelectionId, String callerPackageName);
386 
387     /**
388      * Add an ad selection from outcomes override into the table
389      * ad_selection_from_outcomes_overrides
390      *
391      * @param adSelectionFromOutcomesOverride is the AdSelectionFromOutcomesOverride to add to table
392      *     ad_selection_overrides. If a {@link DBAdSelectionFromOutcomesOverride} object with the
393      *     {@code adSelectionConfigFromOutcomesId} already exists, this will replace the existing
394      *     object.
395      */
396     @Insert(onConflict = OnConflictStrategy.REPLACE)
persistAdSelectionFromOutcomesOverride( DBAdSelectionFromOutcomesOverride adSelectionFromOutcomesOverride)397     public abstract void persistAdSelectionFromOutcomesOverride(
398             DBAdSelectionFromOutcomesOverride adSelectionFromOutcomesOverride);
399 
400     /**
401      * Checks if there is a row in the ad selection override data with the unique key
402      * ad_selection_from_outcomes_config_id
403      *
404      * @param adSelectionFromOutcomesConfigId which is the key to query the corresponding ad
405      *     selection override data.
406      * @return true if row exists, false otherwise
407      */
408     @Query(
409             "SELECT EXISTS(SELECT 1 FROM ad_selection_from_outcomes_overrides WHERE "
410                     + "ad_selection_from_outcomes_config_id = "
411                     + ":adSelectionFromOutcomesConfigId AND app_package_name = :appPackageName "
412                     + "LIMIT 1)")
doesAdSelectionFromOutcomesOverrideExistForPackageName( String adSelectionFromOutcomesConfigId, String appPackageName)413     public abstract boolean doesAdSelectionFromOutcomesOverrideExistForPackageName(
414             String adSelectionFromOutcomesConfigId, String appPackageName);
415 
416     /**
417      * Get ad selection from outcomes selection logic JS override by its unique key and the package
418      * name of the app that created the override.
419      *
420      * @return ad selection override result if exists.
421      */
422     @Query(
423             "SELECT selection_logic_js FROM ad_selection_from_outcomes_overrides WHERE "
424                     + "ad_selection_from_outcomes_config_id = :adSelectionFromOutcomesConfigId "
425                     + "AND app_package_name = :appPackageName")
426     @Nullable
getSelectionLogicOverride( String adSelectionFromOutcomesConfigId, String appPackageName)427     public abstract String getSelectionLogicOverride(
428             String adSelectionFromOutcomesConfigId, String appPackageName);
429 
430     /**
431      * Get ad selection from outcomes signals override by its unique key and the package name of the
432      * app that created the override.
433      *
434      * @return ad selection from outcomes override result if exists.
435      */
436     @Query(
437             "SELECT selection_signals FROM ad_selection_from_outcomes_overrides WHERE"
438                     + " ad_selection_from_outcomes_config_id = :adSelectionFromOutcomesConfigId "
439                     + "AND app_package_name = :appPackageName")
440     @Nullable
getSelectionSignalsOverride( String adSelectionFromOutcomesConfigId, String appPackageName)441     public abstract String getSelectionSignalsOverride(
442             String adSelectionFromOutcomesConfigId, String appPackageName);
443 
444     /**
445      * Clean up selected ad selection from outcomes override data by its {@code
446      * adSelectionFromOutcomesConfigId}
447      *
448      * @param adSelectionFromOutcomesConfigId is to identify the data entries to be removed from the
449      *     ad_selection_overrides table.
450      */
451     @Query(
452             "DELETE FROM ad_selection_from_outcomes_overrides WHERE "
453                     + "ad_selection_from_outcomes_config_id = :adSelectionFromOutcomesConfigId AND "
454                     + "app_package_name = :appPackageName")
removeAdSelectionFromOutcomesOverrideByIdAndPackageName( String adSelectionFromOutcomesConfigId, String appPackageName)455     public abstract void removeAdSelectionFromOutcomesOverrideByIdAndPackageName(
456             String adSelectionFromOutcomesConfigId, String appPackageName);
457 
458     /** Clean up all ad selection from outcomes override data */
459     @Query(
460             "DELETE FROM ad_selection_from_outcomes_overrides WHERE app_package_name = "
461                     + ":appPackageName")
removeAllAdSelectionFromOutcomesOverrides(String appPackageName)462     public abstract void removeAllAdSelectionFromOutcomesOverrides(String appPackageName);
463 
464     /**
465      * Clean up registered_ad_interaction entries in batch if the {@code adSelectionId} no longer
466      * exists in the table ad_selection.
467      */
468     @Query(
469             "DELETE FROM registered_ad_interactions WHERE ad_selection_id NOT IN "
470                     + "( SELECT DISTINCT ad_selection_id "
471                     + "FROM ad_selection "
472                     + "WHERE ad_selection_id is NOT NULL)")
removeExpiredRegisteredAdInteractions()473     public abstract void removeExpiredRegisteredAdInteractions();
474 
475     /** Returns total size of the {@code registered_ad_interaction} table. */
476     @Query("SELECT COUNT(*) FROM registered_ad_interactions")
getTotalNumRegisteredAdInteractions()477     public abstract long getTotalNumRegisteredAdInteractions();
478 
479     /**
480      * Returns total number of the {@code registered_ad_interaction}s that match a given {@code
481      * adSelectionId} and {@code reportingDestination}.
482      */
483     @Query(
484             "SELECT COUNT(*) FROM registered_ad_interactions WHERE ad_selection_id ="
485                     + " :adSelectionId AND destination = :reportingDestination")
getNumRegisteredAdInteractionsPerAdSelectionAndDestination( long adSelectionId, @ReportInteractionRequest.ReportingDestination int reportingDestination)486     public abstract long getNumRegisteredAdInteractionsPerAdSelectionAndDestination(
487             long adSelectionId,
488             @ReportInteractionRequest.ReportingDestination int reportingDestination);
489 
490     /**
491      * Inserts a list of {@link DBRegisteredAdInteraction}s into the database, enforcing these
492      * limitations:
493      *
494      * <p>We will not allow the total size of the {@code registered_ad_interaction} to exceed {@code
495      * maxTotalNumRegisteredInteractions}
496      *
497      * <p>We will not allow the number of registered ad interactions {@code adSelectionId} and
498      * {@code reportingDestination} to exceed {@code maxPerDestinationNumRegisteredInteractions}.
499      *
500      * <p>This transaction is separate in order to minimize the critical region while locking the
501      * database.
502      */
503     @Transaction
safelyInsertRegisteredAdInteractions( long adSelectionId, @NonNull List<DBRegisteredAdInteraction> registeredAdInteractions, long maxTotalNumRegisteredInteractions, long maxPerDestinationNumRegisteredInteractions, int reportingDestination)504     public void safelyInsertRegisteredAdInteractions(
505             long adSelectionId,
506             @NonNull List<DBRegisteredAdInteraction> registeredAdInteractions,
507             long maxTotalNumRegisteredInteractions,
508             long maxPerDestinationNumRegisteredInteractions,
509             int reportingDestination) {
510         long currentNumRegisteredInteractions = getTotalNumRegisteredAdInteractions();
511 
512         if (currentNumRegisteredInteractions >= maxTotalNumRegisteredInteractions) {
513             sLogger.v("Registered Ad Interaction max table size reached! Skipping entire list.");
514             return;
515         }
516 
517         long currentNumRegisteredInteractionsPerDestination =
518                 getNumRegisteredAdInteractionsPerAdSelectionAndDestination(
519                         adSelectionId, reportingDestination);
520 
521         if (currentNumRegisteredInteractionsPerDestination
522                 >= maxPerDestinationNumRegisteredInteractions) {
523             sLogger.v(
524                     "Maximum number of Registered Ad Interactions for this adSelectionId and"
525                             + " reportingDestination reached! Skipping entire list.");
526             return;
527         }
528 
529         long numAvailableRowsInTable =
530                 Math.max(0, maxTotalNumRegisteredInteractions - currentNumRegisteredInteractions);
531 
532         long numAvailableRowsInTablePerAdSelectionIdAndDestination =
533                 Math.max(
534                         0,
535                         maxPerDestinationNumRegisteredInteractions
536                                 - currentNumRegisteredInteractionsPerDestination);
537 
538         int numEntriesToCommit =
539                 (int)
540                         Math.min(
541                                 numAvailableRowsInTablePerAdSelectionIdAndDestination,
542                                 Math.min(registeredAdInteractions.size(), numAvailableRowsInTable));
543         List<DBRegisteredAdInteraction> registeredAdInteractionsToCommit =
544                 registeredAdInteractions.subList(0, numEntriesToCommit);
545 
546         persistDBRegisteredAdInteractions(registeredAdInteractionsToCommit);
547     }
548 }
549