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