• 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.customaudience;
18 
19 import android.adservices.common.AdTechIdentifier;
20 import android.content.pm.PackageManager;
21 import android.net.Uri;
22 
23 import androidx.annotation.NonNull;
24 import androidx.annotation.Nullable;
25 import androidx.room.ColumnInfo;
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.common.CleanupUtils;
34 import com.android.adservices.data.common.DecisionLogic;
35 import com.android.adservices.data.enrollment.EnrollmentDao;
36 import com.android.adservices.service.Flags;
37 import com.android.adservices.service.adselection.JsVersionHelper;
38 import com.android.adservices.service.customaudience.CustomAudienceUpdatableData;
39 import com.android.internal.annotations.VisibleForTesting;
40 
41 import com.google.common.collect.ImmutableMap;
42 
43 import java.time.Instant;
44 import java.util.List;
45 import java.util.Objects;
46 import java.util.Set;
47 
48 /**
49  * DAO abstract class used to access Custom Audience persistent storage.
50  *
51  * <p>Annotations will generate Room-based SQLite Dao impl.
52  */
53 @Dao
54 public abstract class CustomAudienceDao {
55     private static final LoggerFactory.Logger sLogger = LoggerFactory.getFledgeLogger();
56     /**
57      * Add user to a new custom audience. As designed, will override existing one.
58      *
59      * <p>This method is not meant to be used on its own, since custom audiences must be persisted
60      * alongside matching background fetch data. Use {@link
61      * #insertOrOverwriteCustomAudience(DBCustomAudience, Uri)} instead.
62      */
63     @Insert(onConflict = OnConflictStrategy.REPLACE)
persistCustomAudience(@onNull DBCustomAudience customAudience)64     protected abstract void persistCustomAudience(@NonNull DBCustomAudience customAudience);
65 
66     /**
67      * Adds or updates background fetch data for a custom audience.
68      *
69      * <p>This method does not update the corresponding custom audience. Use {@link
70      * #updateCustomAudienceAndBackgroundFetchData(DBCustomAudienceBackgroundFetchData,
71      * CustomAudienceUpdatableData)} to do so safely.
72      */
73     @Insert(onConflict = OnConflictStrategy.REPLACE)
persistCustomAudienceBackgroundFetchData( @onNull DBCustomAudienceBackgroundFetchData fetchData)74     public abstract void persistCustomAudienceBackgroundFetchData(
75             @NonNull DBCustomAudienceBackgroundFetchData fetchData);
76 
77     /**
78      * Adds or updates a given custom audience and background fetch data in a single transaction.
79      *
80      * <p>This transaction is separate in order to minimize the critical region while locking the
81      * database. It is not meant to be exposed or used by itself; use {@link
82      * #insertOrOverwriteCustomAudience(DBCustomAudience, Uri)} instead.
83      */
84     @Transaction
insertOrOverwriteCustomAudienceAndBackgroundFetchData( @onNull DBCustomAudience customAudience, @NonNull DBCustomAudienceBackgroundFetchData fetchData)85     protected void insertOrOverwriteCustomAudienceAndBackgroundFetchData(
86             @NonNull DBCustomAudience customAudience,
87             @NonNull DBCustomAudienceBackgroundFetchData fetchData) {
88         persistCustomAudience(customAudience);
89         persistCustomAudienceBackgroundFetchData(fetchData);
90     }
91 
92     /**
93      * Adds the user to the given custom audience.
94      *
95      * <p>If a custom audience already exists, it is overwritten completely.
96      *
97      * <p>Background fetch data is also created based on the given {@code customAudience} and {@code
98      * dailyUpdateUri} and overwrites any existing background fetch data. This method assumes the
99      * input parameters have already been validated and are correct.
100      */
insertOrOverwriteCustomAudience( @onNull DBCustomAudience customAudience, @NonNull Uri dailyUpdateUri)101     public void insertOrOverwriteCustomAudience(
102             @NonNull DBCustomAudience customAudience, @NonNull Uri dailyUpdateUri) {
103         Objects.requireNonNull(customAudience);
104         Objects.requireNonNull(dailyUpdateUri);
105 
106         Instant eligibleUpdateTime;
107         if (customAudience.getUserBiddingSignals() == null
108                 || customAudience.getTrustedBiddingData() == null
109                 || customAudience.getAds() == null
110                 || customAudience.getAds().isEmpty()) {
111             eligibleUpdateTime = Instant.EPOCH;
112         } else {
113             eligibleUpdateTime =
114                     DBCustomAudienceBackgroundFetchData
115                             .computeNextEligibleUpdateTimeAfterSuccessfulUpdate(
116                                     customAudience.getCreationTime());
117         }
118 
119         DBCustomAudienceBackgroundFetchData fetchData =
120                 DBCustomAudienceBackgroundFetchData.builder()
121                         .setOwner(customAudience.getOwner())
122                         .setBuyer(customAudience.getBuyer())
123                         .setName(customAudience.getName())
124                         .setDailyUpdateUri(dailyUpdateUri)
125                         .setEligibleUpdateTime(eligibleUpdateTime)
126                         .build();
127 
128         insertOrOverwriteCustomAudienceAndBackgroundFetchData(customAudience, fetchData);
129     }
130 
131     /**
132      * Updates a custom audience and its background fetch data based on the given {@link
133      * CustomAudienceUpdatableData} in a single transaction.
134      *
135      * <p>If no custom audience is found corresponding to the given {@link
136      * DBCustomAudienceBackgroundFetchData}, no action is taken.
137      */
138     @Transaction
updateCustomAudienceAndBackgroundFetchData( @onNull DBCustomAudienceBackgroundFetchData fetchData, @NonNull CustomAudienceUpdatableData updatableData)139     public void updateCustomAudienceAndBackgroundFetchData(
140             @NonNull DBCustomAudienceBackgroundFetchData fetchData,
141             @NonNull CustomAudienceUpdatableData updatableData) {
142         Objects.requireNonNull(fetchData);
143         Objects.requireNonNull(updatableData);
144 
145         DBCustomAudience customAudience =
146                 getCustomAudienceByPrimaryKey(
147                         fetchData.getOwner(), fetchData.getBuyer(), fetchData.getName());
148 
149         if (customAudience == null) {
150             // This custom audience could have been cleaned up while it was being updated
151             return;
152         }
153 
154         customAudience = customAudience.copyWithUpdatableData(updatableData);
155 
156         persistCustomAudience(customAudience);
157         persistCustomAudienceBackgroundFetchData(fetchData);
158     }
159 
160     /** Get count of custom audience. */
161     @Query("SELECT COUNT(*) FROM custom_audience")
getCustomAudienceCount()162     public abstract long getCustomAudienceCount();
163 
164     /** Get count of custom audience of a given owner. */
165     @Query("SELECT COUNT(*) FROM custom_audience WHERE owner=:owner")
getCustomAudienceCountForOwner(String owner)166     public abstract long getCustomAudienceCountForOwner(String owner);
167 
168     /** Get the total number of distinct custom audience owner. */
169     @Query("SELECT COUNT(DISTINCT owner) FROM custom_audience")
getCustomAudienceOwnerCount()170     public abstract long getCustomAudienceOwnerCount();
171 
172     /**
173      * Get the count of total custom audience, the count for the given owner and the count of
174      * distinct owner in one transaction.
175      *
176      * @param owner the owner we need check the count against.
177      * @return the aggregated data of custom audience count
178      */
179     @Transaction
180     @NonNull
getCustomAudienceStats(@onNull String owner)181     public CustomAudienceStats getCustomAudienceStats(@NonNull String owner) {
182         Objects.requireNonNull(owner);
183 
184         long customAudienceCount = getCustomAudienceCount();
185         long customAudienceCountPerOwner = getCustomAudienceCountForOwner(owner);
186         long ownerCount = getCustomAudienceOwnerCount();
187 
188         // TODO(b/255780705): Add buyer and per-buyer stats
189         return CustomAudienceStats.builder()
190                 .setOwner(owner)
191                 .setTotalCustomAudienceCount(customAudienceCount)
192                 .setPerOwnerCustomAudienceCount(customAudienceCountPerOwner)
193                 .setTotalOwnerCount(ownerCount)
194                 .build();
195     }
196 
197     /**
198      * Add a custom audience override into the table custom_audience_overrides
199      *
200      * @param customAudienceOverride is the CustomAudienceOverride to add to table
201      *     custom_audience_overrides. If a {@link DBCustomAudienceOverride} object with the primary
202      *     key already exists, this will replace the existing object.
203      */
204     @Insert(onConflict = OnConflictStrategy.REPLACE)
persistCustomAudienceOverride( DBCustomAudienceOverride customAudienceOverride)205     public abstract void persistCustomAudienceOverride(
206             DBCustomAudienceOverride customAudienceOverride);
207 
208     /**
209      * Checks if there is a row in the custom audience override data with the unique key combination
210      * of owner, buyer, and name
211      *
212      * @return true if row exists, false otherwise
213      */
214     @Query(
215             "SELECT EXISTS(SELECT 1 FROM custom_audience_overrides WHERE owner = :owner "
216                     + "AND buyer = :buyer AND name = :name LIMIT 1)")
doesCustomAudienceOverrideExist( @onNull String owner, @NonNull AdTechIdentifier buyer, @NonNull String name)217     public abstract boolean doesCustomAudienceOverrideExist(
218             @NonNull String owner, @NonNull AdTechIdentifier buyer, @NonNull String name);
219 
220     /**
221      * Get custom audience by its unique key.
222      *
223      * @return custom audience result if exists.
224      */
225     @Query("SELECT * FROM custom_audience WHERE owner = :owner AND buyer = :buyer AND name = :name")
226     @Nullable
227     @VisibleForTesting
getCustomAudienceByPrimaryKey( @onNull String owner, @NonNull AdTechIdentifier buyer, @NonNull String name)228     public abstract DBCustomAudience getCustomAudienceByPrimaryKey(
229             @NonNull String owner, @NonNull AdTechIdentifier buyer, @NonNull String name);
230 
231     /**
232      * Get custom audience background fetch data by its unique key.
233      *
234      * @return custom audience background fetch data if it exists
235      */
236     @Query(
237             "SELECT * FROM custom_audience_background_fetch_data "
238                     + "WHERE owner = :owner AND buyer = :buyer AND name = :name")
239     @Nullable
240     @VisibleForTesting
241     public abstract DBCustomAudienceBackgroundFetchData
getCustomAudienceBackgroundFetchDataByPrimaryKey( @onNull String owner, @NonNull AdTechIdentifier buyer, @NonNull String name)242             getCustomAudienceBackgroundFetchDataByPrimaryKey(
243                     @NonNull String owner, @NonNull AdTechIdentifier buyer, @NonNull String name);
244 
245     /**
246      * Get custom audience JS override by its unique key.
247      *
248      * <p>This method is not intended to be called on its own. Please use {@link
249      * #getBiddingLogicUriOverride(String, AdTechIdentifier, String, String)} instead.
250      *
251      * @return custom audience override result if exists.
252      */
253     @Query(
254             "SELECT bidding_logic as bidding_logic_js, bidding_logic_version as"
255                     + " buyer_bidding_logic_version FROM custom_audience_overrides WHERE owner ="
256                     + " :owner AND buyer = :buyer AND name = :name AND app_package_name="
257                     + " :appPackageName")
258     @Nullable
getBiddingLogicUriOverrideInternal( @onNull String owner, @NonNull AdTechIdentifier buyer, @NonNull String name, @NonNull String appPackageName)259     protected abstract BiddingLogicJsWithVersion getBiddingLogicUriOverrideInternal(
260             @NonNull String owner,
261             @NonNull AdTechIdentifier buyer,
262             @NonNull String name,
263             @NonNull String appPackageName);
264 
265     /**
266      * Get custom audience JS override by its unique key.
267      *
268      * @return custom audience override result if exists.
269      */
getBiddingLogicUriOverride( @onNull String owner, @NonNull AdTechIdentifier buyer, @NonNull String name, @NonNull String appPackageName)270     public DecisionLogic getBiddingLogicUriOverride(
271             @NonNull String owner,
272             @NonNull AdTechIdentifier buyer,
273             @NonNull String name,
274             @NonNull String appPackageName) {
275         BiddingLogicJsWithVersion biddingLogicJsWithVersion =
276                 getBiddingLogicUriOverrideInternal(owner, buyer, name, appPackageName);
277 
278         if (Objects.isNull(biddingLogicJsWithVersion)) {
279             return null;
280         }
281 
282         ImmutableMap.Builder<Integer, Long> versionMap = new ImmutableMap.Builder<>();
283         if (Objects.nonNull(biddingLogicJsWithVersion.getBuyerBiddingLogicVersion())) {
284             versionMap.put(
285                     JsVersionHelper.JS_PAYLOAD_TYPE_BUYER_BIDDING_LOGIC_JS,
286                     biddingLogicJsWithVersion.getBuyerBiddingLogicVersion());
287         }
288 
289         return DecisionLogic.create(
290                 biddingLogicJsWithVersion.getBiddingLogicJs(), versionMap.build());
291     }
292 
293     /**
294      * Get trusted bidding data override by its unique key.
295      *
296      * @return custom audience override result if exists.
297      */
298     @Query(
299             "SELECT trusted_bidding_data FROM custom_audience_overrides WHERE owner = :owner "
300                     + "AND buyer = :buyer AND name = :name AND app_package_name= :appPackageName")
301     @Nullable
getTrustedBiddingDataOverride( @onNull String owner, @NonNull AdTechIdentifier buyer, @NonNull String name, @NonNull String appPackageName)302     public abstract String getTrustedBiddingDataOverride(
303             @NonNull String owner,
304             @NonNull AdTechIdentifier buyer,
305             @NonNull String name,
306             @NonNull String appPackageName);
307 
308     /** Delete the custom audience given owner, buyer, and name. */
309     @Query("DELETE FROM custom_audience WHERE owner = :owner AND buyer = :buyer AND name = :name")
deleteCustomAudienceByPrimaryKey( @onNull String owner, @NonNull AdTechIdentifier buyer, @NonNull String name)310     protected abstract void deleteCustomAudienceByPrimaryKey(
311             @NonNull String owner, @NonNull AdTechIdentifier buyer, @NonNull String name);
312 
313     /** Delete background fetch data for the custom audience given owner, buyer, and name. */
314     @Query(
315             "DELETE FROM custom_audience_background_fetch_data WHERE owner = :owner "
316                     + "AND buyer = :buyer AND name = :name")
deleteCustomAudienceBackgroundFetchDataByPrimaryKey( @onNull String owner, @NonNull AdTechIdentifier buyer, @NonNull String name)317     protected abstract void deleteCustomAudienceBackgroundFetchDataByPrimaryKey(
318             @NonNull String owner, @NonNull AdTechIdentifier buyer, @NonNull String name);
319 
320     /**
321      * Delete all custom audience data corresponding to the given {@code owner}, {@code buyer}, and
322      * {@code name} in a single transaction.
323      */
324     @Transaction
deleteAllCustomAudienceDataByPrimaryKey( @onNull String owner, @NonNull AdTechIdentifier buyer, @NonNull String name)325     public void deleteAllCustomAudienceDataByPrimaryKey(
326             @NonNull String owner, @NonNull AdTechIdentifier buyer, @NonNull String name) {
327         deleteCustomAudienceByPrimaryKey(owner, buyer, name);
328         deleteCustomAudienceBackgroundFetchDataByPrimaryKey(owner, buyer, name);
329     }
330 
331     /**
332      * Deletes all custom audiences which are expired, where the custom audiences' expiration times
333      * match or precede the given {@code expiryTime}.
334      *
335      * <p>This method is not intended to be called on its own. Please use {@link
336      * #deleteAllExpiredCustomAudienceData(Instant)} instead.
337      *
338      * @return the number of deleted custom audiences
339      */
340     @Query("DELETE FROM custom_audience WHERE expiration_time <= :expiryTime")
deleteAllExpiredCustomAudiences(@onNull Instant expiryTime)341     protected abstract int deleteAllExpiredCustomAudiences(@NonNull Instant expiryTime);
342 
343     /**
344      * Deletes background fetch data for all custom audiences which are expired, where the custom
345      * audiences' expiration times match or precede the given {@code expiryTime}.
346      *
347      * <p>This method is not intended to be called on its own. Please use {@link
348      * #deleteAllExpiredCustomAudienceData(Instant)} instead.
349      */
350     @Query(
351             "DELETE FROM custom_audience_background_fetch_data WHERE ROWID IN "
352                     + "(SELECT bgf.ROWID FROM custom_audience_background_fetch_data AS bgf "
353                     + "INNER JOIN custom_audience AS ca "
354                     + "ON bgf.buyer = ca.buyer AND bgf.owner = ca.owner AND bgf.name = ca.name "
355                     + "WHERE expiration_time <= :expiryTime)")
deleteAllExpiredCustomAudienceBackgroundFetchData( @onNull Instant expiryTime)356     protected abstract void deleteAllExpiredCustomAudienceBackgroundFetchData(
357             @NonNull Instant expiryTime);
358 
359     /**
360      * Deletes all expired custom audience data in a single transaction, where the custom audiences'
361      * expiration times match or precede the given {@code expiryTime}.
362      *
363      * @return the number of deleted custom audiences
364      */
365     @Transaction
deleteAllExpiredCustomAudienceData(@onNull Instant expiryTime)366     public int deleteAllExpiredCustomAudienceData(@NonNull Instant expiryTime) {
367         deleteAllExpiredCustomAudienceBackgroundFetchData(expiryTime);
368         return deleteAllExpiredCustomAudiences(expiryTime);
369     }
370 
371     /** Returns the set of all unique owner apps in the custom audience table. */
372     @Query("SELECT DISTINCT owner FROM custom_audience")
getAllCustomAudienceOwners()373     public abstract List<String> getAllCustomAudienceOwners();
374 
375     /**
376      * Deletes all custom audiences belonging to any app in the given set of {@code ownersToRemove}.
377      *
378      * <p>This method is not intended to be called on its own. Please use {@link
379      * #deleteAllDisallowedOwnerCustomAudienceData(PackageManager, Flags)} instead.
380      *
381      * @return the number of deleted custom audiences
382      */
383     @Query("DELETE FROM custom_audience WHERE owner IN (:ownersToRemove)")
deleteCustomAudiencesByOwner(@onNull List<String> ownersToRemove)384     protected abstract int deleteCustomAudiencesByOwner(@NonNull List<String> ownersToRemove);
385 
386     /**
387      * Deletes all custom audience background fetch data belonging to any app in the given set of
388      * {@code ownersToRemove}.
389      *
390      * <p>This method is not intended to be called on its own. Please use {@link
391      * #deleteAllDisallowedOwnerCustomAudienceData(PackageManager, Flags)} instead.
392      */
393     @Query("DELETE FROM custom_audience_background_fetch_data WHERE owner IN (:ownersToRemove)")
deleteCustomAudienceBackgroundFetchDataByOwner( @onNull List<String> ownersToRemove)394     protected abstract void deleteCustomAudienceBackgroundFetchDataByOwner(
395             @NonNull List<String> ownersToRemove);
396 
397     /**
398      * Deletes all custom audience data belonging to disallowed owner apps in a single transaction,
399      * where the custom audiences' owner apps cannot be found in the installed list or where the
400      * owner apps are not found in the app allowlist.
401      *
402      * @return a {@link CustomAudienceStats} object containing only the number of deleted custom
403      *     audiences and the number of disallowed owner apps found
404      */
405     @Transaction
406     @NonNull
deleteAllDisallowedOwnerCustomAudienceData( @onNull PackageManager packageManager, @NonNull Flags flags)407     public CustomAudienceStats deleteAllDisallowedOwnerCustomAudienceData(
408             @NonNull PackageManager packageManager, @NonNull Flags flags) {
409         Objects.requireNonNull(packageManager);
410         Objects.requireNonNull(flags);
411         List<String> ownersToRemove = getAllCustomAudienceOwners();
412 
413         CleanupUtils.removeAllowedPackages(ownersToRemove, packageManager, flags);
414 
415         long numDisallowedOwnersFound = ownersToRemove.size();
416         long numRemovedCustomAudiences = 0;
417         if (!ownersToRemove.isEmpty()) {
418             deleteCustomAudienceBackgroundFetchDataByOwner(ownersToRemove);
419             numRemovedCustomAudiences = deleteCustomAudiencesByOwner(ownersToRemove);
420         }
421 
422         return CustomAudienceStats.builder()
423                 .setTotalCustomAudienceCount(numRemovedCustomAudiences)
424                 .setTotalOwnerCount(numDisallowedOwnersFound)
425                 .build();
426     }
427 
428     /** Returns the set of all unique buyer ad techs in the custom audience table. */
429     @Query("SELECT DISTINCT buyer FROM custom_audience")
getAllCustomAudienceBuyers()430     public abstract List<AdTechIdentifier> getAllCustomAudienceBuyers();
431 
432     /**
433      * Deletes all custom audiences belonging to any ad tech in the given set of {@code
434      * buyersToRemove}.
435      *
436      * <p>This method is not intended to be called on its own. Please use {@link
437      * #deleteAllDisallowedBuyerCustomAudienceData(EnrollmentDao, Flags)} instead.
438      *
439      * @return the number of deleted custom audiences
440      */
441     @Query("DELETE FROM custom_audience WHERE buyer IN (:buyersToRemove)")
deleteCustomAudiencesByBuyer( @onNull List<AdTechIdentifier> buyersToRemove)442     protected abstract int deleteCustomAudiencesByBuyer(
443             @NonNull List<AdTechIdentifier> buyersToRemove);
444 
445     /**
446      * Deletes all custom audience background fetch data belonging to any ad tech in the given set
447      * of {@code buyersToRemove}.
448      *
449      * <p>This method is not intended to be called on its own. Please use {@link
450      * #deleteAllDisallowedBuyerCustomAudienceData(EnrollmentDao, Flags)} instead.
451      */
452     @Query("DELETE FROM custom_audience_background_fetch_data WHERE buyer IN (:buyersToRemove)")
deleteCustomAudienceBackgroundFetchDataByBuyer( @onNull List<AdTechIdentifier> buyersToRemove)453     protected abstract void deleteCustomAudienceBackgroundFetchDataByBuyer(
454             @NonNull List<AdTechIdentifier> buyersToRemove);
455 
456     /**
457      * Deletes all custom audience data belonging to disallowed buyer ad techs in a single
458      * transaction, where the custom audiences' buyer ad techs cannot be found in the enrollment
459      * database.
460      *
461      * @return a {@link CustomAudienceStats} object containing only the number of deleted custom
462      *     audiences and the number of disallowed owner apps found
463      */
464     @Transaction
465     @NonNull
deleteAllDisallowedBuyerCustomAudienceData( @onNull EnrollmentDao enrollmentDao, @NonNull Flags flags)466     public CustomAudienceStats deleteAllDisallowedBuyerCustomAudienceData(
467             @NonNull EnrollmentDao enrollmentDao, @NonNull Flags flags) {
468         Objects.requireNonNull(enrollmentDao);
469         Objects.requireNonNull(flags);
470 
471         if (flags.getDisableFledgeEnrollmentCheck()) {
472             sLogger.d("FLEDGE enrollment check disabled; skipping enrolled buyer cleanup");
473             return CustomAudienceStats.builder()
474                     .setTotalCustomAudienceCount(0)
475                     .setTotalBuyerCount(0)
476                     .build();
477         }
478 
479         List<AdTechIdentifier> buyersToRemove = getAllCustomAudienceBuyers();
480 
481         if (!buyersToRemove.isEmpty()) {
482             Set<AdTechIdentifier> allowedAdTechs = enrollmentDao.getAllFledgeEnrolledAdTechs();
483             buyersToRemove.removeAll(allowedAdTechs);
484         }
485 
486         long numDisallowedBuyersFound = buyersToRemove.size();
487         long numRemovedCustomAudiences = 0;
488         if (!buyersToRemove.isEmpty()) {
489             deleteCustomAudienceBackgroundFetchDataByBuyer(buyersToRemove);
490             numRemovedCustomAudiences = deleteCustomAudiencesByBuyer(buyersToRemove);
491         }
492 
493         return CustomAudienceStats.builder()
494                 .setTotalCustomAudienceCount(numRemovedCustomAudiences)
495                 .setTotalBuyerCount(numDisallowedBuyersFound)
496                 .build();
497     }
498 
499     /**
500      * Deletes ALL custom audiences from the table.
501      *
502      * <p>This method is not intended to be called on its own. Please use {@link
503      * #deleteAllCustomAudienceData()} instead.
504      */
505     @Query("DELETE FROM custom_audience")
deleteAllCustomAudiences()506     protected abstract void deleteAllCustomAudiences();
507 
508     /**
509      * Deletes ALL custom audience background fetch data from the table.
510      *
511      * <p>This method is not intended to be called on its own. Please use {@link
512      * #deleteAllCustomAudienceData()} instead.
513      */
514     @Query("DELETE FROM custom_audience_background_fetch_data")
deleteAllCustomAudienceBackgroundFetchData()515     protected abstract void deleteAllCustomAudienceBackgroundFetchData();
516 
517     /**
518      * Deletes ALL custom audience overrides from the table.
519      *
520      * <p>This method is not intended to be called on its own. Please use {@link
521      * #deleteAllCustomAudienceData()} instead.
522      */
523     @Query("DELETE FROM custom_audience_overrides")
deleteAllCustomAudienceOverrides()524     protected abstract void deleteAllCustomAudienceOverrides();
525 
526     /** Deletes ALL custom audience data from the database in a single transaction. */
527     @Transaction
deleteAllCustomAudienceData()528     public void deleteAllCustomAudienceData() {
529         deleteAllCustomAudiences();
530         deleteAllCustomAudienceBackgroundFetchData();
531         deleteAllCustomAudienceOverrides();
532     }
533 
534     /**
535      * Deletes all custom audiences belonging to the {@code owner} application from the table.
536      *
537      * <p>This method is not intended to be called on its own. Please use {@link
538      * #deleteCustomAudienceDataByOwner(String)} instead.
539      */
540     @Query("DELETE FROM custom_audience WHERE owner = :owner")
deleteCustomAudiencesByOwner(@onNull String owner)541     protected abstract void deleteCustomAudiencesByOwner(@NonNull String owner);
542 
543     /**
544      * Deletes all custom audience background fetch data belonging to the {@code owner} application
545      * from the table.
546      *
547      * <p>This method is not intended to be called on its own. Please use {@link
548      * #deleteCustomAudienceDataByOwner(String)} instead.
549      */
550     @Query("DELETE FROM custom_audience_background_fetch_data WHERE owner = :owner")
deleteCustomAudienceBackgroundFetchDataByOwner(@onNull String owner)551     protected abstract void deleteCustomAudienceBackgroundFetchDataByOwner(@NonNull String owner);
552 
553     /**
554      * Deletes all custom audience overrides belonging to the {@code owner} application from the
555      * table.
556      *
557      * <p>This method is not intended to be called on its own. Please use {@link
558      * #deleteCustomAudienceDataByOwner(String)} instead.
559      */
560     @Query("DELETE FROM custom_audience_overrides WHERE owner = :owner")
deleteCustomAudienceOverridesByOwner(@onNull String owner)561     protected abstract void deleteCustomAudienceOverridesByOwner(@NonNull String owner);
562 
563     /**
564      * Deletes all custom audience data belonging to the {@code owner} application from the database
565      * in a single transaction.
566      */
567     @Transaction
deleteCustomAudienceDataByOwner(@onNull String owner)568     public void deleteCustomAudienceDataByOwner(@NonNull String owner) {
569         deleteCustomAudiencesByOwner(owner);
570         deleteCustomAudienceBackgroundFetchDataByOwner(owner);
571         deleteCustomAudienceOverridesByOwner(owner);
572     }
573 
574     /** Clean up selected custom audience override data by its primary key */
575     @Query(
576             "DELETE FROM custom_audience_overrides WHERE owner = :owner AND buyer = :buyer "
577                     + "AND name = :name AND app_package_name = :appPackageName")
removeCustomAudienceOverrideByPrimaryKeyAndPackageName( @onNull String owner, @NonNull AdTechIdentifier buyer, @NonNull String name, @NonNull String appPackageName)578     public abstract void removeCustomAudienceOverrideByPrimaryKeyAndPackageName(
579             @NonNull String owner,
580             @NonNull AdTechIdentifier buyer,
581             @NonNull String name,
582             @NonNull String appPackageName);
583 
584     /** Clean up all custom audience override data for the given package name. */
585     @Query("DELETE FROM custom_audience_overrides WHERE app_package_name = :appPackageName")
removeCustomAudienceOverridesByPackageName(@onNull String appPackageName)586     public abstract void removeCustomAudienceOverridesByPackageName(@NonNull String appPackageName);
587 
588     /**
589      * Fetch all the Custom Audience corresponding to the buyers
590      *
591      * @param buyers associated with the Custom Audience
592      * @param currentTime to compare against CA time values and find an active CA
593      * @return All the Custom Audience that represent given buyers
594      */
595     @Query(
596             "SELECT * FROM custom_audience WHERE buyer in (:buyers) AND activation_time <="
597                     + " (:currentTime) AND (:currentTime) < expiration_time AND"
598                     + " (last_ads_and_bidding_data_updated_time + (:activeWindowTimeMs)) >="
599                     + " (:currentTime) AND user_bidding_signals IS NOT NULL AND"
600                     + " trusted_bidding_data_uri IS NOT NULL AND ads IS NOT NULL ")
601     @Nullable
getActiveCustomAudienceByBuyers( List<AdTechIdentifier> buyers, Instant currentTime, long activeWindowTimeMs)602     public abstract List<DBCustomAudience> getActiveCustomAudienceByBuyers(
603             List<AdTechIdentifier> buyers, Instant currentTime, long activeWindowTimeMs);
604 
605     /**
606      * Gets up to {@code maxRowsReturned} rows of {@link DBCustomAudienceBackgroundFetchData} which
607      * correspond to custom audiences that are active, not expired, and eligible for update.
608      */
609     @Query(
610             "SELECT bgf.* FROM custom_audience_background_fetch_data AS bgf "
611                     + "INNER JOIN custom_audience AS ca "
612                     + "ON bgf.buyer = ca.buyer AND bgf.owner = ca.owner AND bgf.name = ca.name "
613                     + "WHERE bgf.eligible_update_time <= :currentTime "
614                     + "AND ca.activation_time <= :currentTime "
615                     + "AND :currentTime < ca.expiration_time "
616                     + "ORDER BY ca.last_ads_and_bidding_data_updated_time ASC "
617                     + "LIMIT :maxRowsReturned")
618     @NonNull
619     public abstract List<DBCustomAudienceBackgroundFetchData>
getActiveEligibleCustomAudienceBackgroundFetchData( @onNull Instant currentTime, long maxRowsReturned)620             getActiveEligibleCustomAudienceBackgroundFetchData(
621                     @NonNull Instant currentTime, long maxRowsReturned);
622 
623     /**
624      * Gets the number of all {@link DBCustomAudienceBackgroundFetchData} for custom audiences that
625      * are active, not expired, and eligible for update.
626      */
627     @Query(
628             "SELECT COUNT(DISTINCT bgf.ROWID) FROM custom_audience_background_fetch_data AS bgf "
629                     + "INNER JOIN custom_audience AS ca "
630                     + "ON bgf.buyer = ca.buyer AND bgf.owner = ca.owner AND bgf.name = ca.name "
631                     + "WHERE bgf.eligible_update_time <= :currentTime "
632                     + "AND ca.activation_time <= :currentTime "
633                     + "AND :currentTime < ca.expiration_time")
getNumActiveEligibleCustomAudienceBackgroundFetchData( @onNull Instant currentTime)634     public abstract int getNumActiveEligibleCustomAudienceBackgroundFetchData(
635             @NonNull Instant currentTime);
636 
637     @VisibleForTesting
638     static class BiddingLogicJsWithVersion {
639         @ColumnInfo(name = "bidding_logic_js")
640         @NonNull
641         String mBiddingLogicJs;
642 
643         @ColumnInfo(name = "buyer_bidding_logic_version")
644         @Nullable
645         Long mBuyerBiddingLogicVersion;
646 
BiddingLogicJsWithVersion( @onNull String biddingLogicJs, @Nullable Long buyerBiddingLogicVersion)647         BiddingLogicJsWithVersion(
648                 @NonNull String biddingLogicJs, @Nullable Long buyerBiddingLogicVersion) {
649             this.mBiddingLogicJs = biddingLogicJs;
650             this.mBuyerBiddingLogicVersion = buyerBiddingLogicVersion;
651         }
652 
653         @NonNull
getBiddingLogicJs()654         public String getBiddingLogicJs() {
655             return mBiddingLogicJs;
656         }
657 
658         @Nullable
getBuyerBiddingLogicVersion()659         public Long getBuyerBiddingLogicVersion() {
660             return mBuyerBiddingLogicVersion;
661         }
662     }
663 }
664