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 static com.android.adservices.service.stats.AdServicesStatsLog.AD_SERVICES_ERROR_REPORTED__ERROR_CODE__CUSTOM_AUDIENCE_DAO_FAILED_DUE_TO_PENDING_SCHEDULE; 20 import static com.android.adservices.service.stats.AdServicesStatsLog.AD_SERVICES_ERROR_REPORTED__ERROR_CODE__CUSTOM_AUDIENCE_DAO_QUARANTINE_TABLE_MAX_REACHED; 21 import static com.android.adservices.service.stats.AdServicesStatsLog.AD_SERVICES_ERROR_REPORTED__PPAPI_NAME__FLEDGE; 22 import static com.android.adservices.service.stats.AdServicesStatsLog.AD_SERVICES_ERROR_REPORTED__PPAPI_NAME__SCHEDULE_CUSTOM_AUDIENCE_UPDATE; 23 import static com.android.adservices.service.stats.AdsRelevanceStatusUtils.SCHEDULE_CA_UPDATE_EXISTING_UPDATE_STATUS_DID_OVERWRITE_EXISTING_UPDATE; 24 import static com.android.adservices.service.stats.AdsRelevanceStatusUtils.SCHEDULE_CA_UPDATE_EXISTING_UPDATE_STATUS_NO_EXISTING_UPDATE; 25 import static com.android.adservices.service.stats.AdsRelevanceStatusUtils.SCHEDULE_CA_UPDATE_EXISTING_UPDATE_STATUS_REJECTED_BY_EXISTING_UPDATE; 26 27 import android.adservices.common.AdTechIdentifier; 28 import android.adservices.common.ComponentAdData; 29 import android.adservices.customaudience.PartialCustomAudience; 30 import android.content.pm.PackageManager; 31 import android.net.Uri; 32 33 import androidx.annotation.NonNull; 34 import androidx.annotation.Nullable; 35 import androidx.room.ColumnInfo; 36 import androidx.room.Dao; 37 import androidx.room.Delete; 38 import androidx.room.Insert; 39 import androidx.room.OnConflictStrategy; 40 import androidx.room.Query; 41 import androidx.room.Transaction; 42 43 import com.android.adservices.LoggerFactory; 44 import com.android.adservices.data.common.CleanupUtils; 45 import com.android.adservices.data.common.DecisionLogic; 46 import com.android.adservices.data.enrollment.EnrollmentDao; 47 import com.android.adservices.errorlogging.ErrorLogUtil; 48 import com.android.adservices.service.Flags; 49 import com.android.adservices.service.adselection.JsVersionHelper; 50 import com.android.adservices.service.customaudience.CustomAudienceUpdatableData; 51 import com.android.adservices.service.exception.PersistScheduleCAUpdateException; 52 import com.android.adservices.service.stats.ScheduledCustomAudienceUpdateScheduleAttemptedStats; 53 import com.android.internal.annotations.VisibleForTesting; 54 55 import com.google.common.collect.ImmutableMap; 56 57 import java.time.Instant; 58 import java.util.ArrayList; 59 import java.util.Arrays; 60 import java.util.List; 61 import java.util.Locale; 62 import java.util.Objects; 63 import java.util.Set; 64 import java.util.stream.Collectors; 65 66 /** 67 * DAO abstract class used to access Custom Audience persistent storage. 68 * 69 * <p>Annotations will generate Room-based SQLite Dao impl. 70 */ 71 @Dao 72 public abstract class CustomAudienceDao { 73 private static final LoggerFactory.Logger sLogger = LoggerFactory.getFledgeLogger(); 74 /** 75 * Add user to a new custom audience. As designed, will override existing one. 76 * 77 * <p>This method is not meant to be used on its own, since custom audiences must be persisted 78 * alongside matching background fetch data. Use {@link 79 * #insertOrOverwriteCustomAudience(DBCustomAudience, Uri)} instead. 80 */ 81 @Insert(onConflict = OnConflictStrategy.REPLACE) persistCustomAudience(@onNull DBCustomAudience customAudience)82 protected abstract void persistCustomAudience(@NonNull DBCustomAudience customAudience); 83 84 /** 85 * Adds or updates background fetch data for a custom audience. 86 * 87 * <p>This method does not update the corresponding custom audience. Use {@link 88 * #updateCustomAudienceAndBackgroundFetchData(DBCustomAudienceBackgroundFetchData, 89 * CustomAudienceUpdatableData)} to do so safely. 90 */ 91 @Insert(onConflict = OnConflictStrategy.REPLACE) persistCustomAudienceBackgroundFetchData( @onNull DBCustomAudienceBackgroundFetchData fetchData)92 public abstract void persistCustomAudienceBackgroundFetchData( 93 @NonNull DBCustomAudienceBackgroundFetchData fetchData); 94 95 /** 96 * Adds a new {@link DBCustomAudienceQuarantine} entry to the {@code custom_audience_quarantine} 97 * table. 98 * 99 * <p>This method is not meant to be used on its own, since it doesn't take into account the 100 * maximum size of {@code custom_audience_quarantine}. Use {@link 101 * #safelyInsertCustomAudienceQuarantine} instead. 102 */ 103 @Insert(onConflict = OnConflictStrategy.REPLACE) persistCustomAudienceQuarantineData( DBCustomAudienceQuarantine dbCustomAudienceQuarantine)104 abstract void persistCustomAudienceQuarantineData( 105 DBCustomAudienceQuarantine dbCustomAudienceQuarantine); 106 107 /** 108 * Adds or updates a given custom audience, background fetch data, and component ads in a single 109 * transaction. 110 * 111 * <p>This transaction is separate in order to minimize the critical region while locking the 112 * database. It is not meant to be exposed or used by itself; use {@link 113 * #insertOrOverwriteCustomAudience(DBCustomAudience, Uri)} instead. 114 */ 115 @Transaction insertOrOverwriteCustomAudienceAndBackgroundFetchData( @onNull DBCustomAudience customAudience, @NonNull DBCustomAudienceBackgroundFetchData fetchData, List<ComponentAdData> componentAds)116 protected void insertOrOverwriteCustomAudienceAndBackgroundFetchData( 117 @NonNull DBCustomAudience customAudience, 118 @NonNull DBCustomAudienceBackgroundFetchData fetchData, 119 List<ComponentAdData> componentAds) { 120 persistCustomAudience(customAudience); 121 persistCustomAudienceBackgroundFetchData(fetchData); 122 123 sLogger.v("Inserting Component Ads in the DB: %s", componentAds); 124 insertAndOverwriteComponentAds( 125 componentAds, 126 customAudience.getOwner(), 127 customAudience.getBuyer(), 128 customAudience.getName()); 129 } 130 131 /** 132 * Adds the user to the given custom audience. 133 * 134 * <p>If a custom audience already exists, it is overwritten completely. 135 * 136 * <p>Background fetch data is also created based on the given {@code customAudience} and {@code 137 * dailyUpdateUri} and overwrites any existing background fetch data. This method assumes the 138 * input parameters have already been validated and are correct. 139 * 140 * <p>Also adds component ads. 141 */ insertOrOverwriteCustomAudience( @onNull DBCustomAudience customAudience, @NonNull Uri dailyUpdateUri, boolean debuggable, List<ComponentAdData> componentAds)142 public void insertOrOverwriteCustomAudience( 143 @NonNull DBCustomAudience customAudience, 144 @NonNull Uri dailyUpdateUri, 145 boolean debuggable, 146 List<ComponentAdData> componentAds) { 147 Objects.requireNonNull(customAudience); 148 Objects.requireNonNull(dailyUpdateUri); 149 150 Instant eligibleUpdateTime; 151 if (customAudience.getUserBiddingSignals() == null 152 || customAudience.getTrustedBiddingData() == null 153 || customAudience.getAds() == null 154 || customAudience.getAds().isEmpty()) { 155 eligibleUpdateTime = Instant.EPOCH; 156 } else { 157 eligibleUpdateTime = 158 DBCustomAudienceBackgroundFetchData 159 .computeNextEligibleUpdateTimeAfterSuccessfulUpdate( 160 customAudience.getCreationTime()); 161 } 162 163 DBCustomAudienceBackgroundFetchData fetchData = 164 DBCustomAudienceBackgroundFetchData.builder() 165 .setOwner(customAudience.getOwner()) 166 .setBuyer(customAudience.getBuyer()) 167 .setName(customAudience.getName()) 168 .setDailyUpdateUri(dailyUpdateUri) 169 .setEligibleUpdateTime(eligibleUpdateTime) 170 .setIsDebuggable(debuggable) 171 .build(); 172 173 insertOrOverwriteCustomAudienceAndBackgroundFetchData( 174 customAudience, fetchData, componentAds); 175 } 176 177 /** 178 * Updates a custom audience and its background fetch data based on the given {@link 179 * CustomAudienceUpdatableData} in a single transaction. 180 * 181 * <p>If no custom audience is found corresponding to the given {@link 182 * DBCustomAudienceBackgroundFetchData}, no action is taken. 183 */ 184 @Transaction updateCustomAudienceAndBackgroundFetchData( @onNull DBCustomAudienceBackgroundFetchData fetchData, @NonNull CustomAudienceUpdatableData updatableData)185 public void updateCustomAudienceAndBackgroundFetchData( 186 @NonNull DBCustomAudienceBackgroundFetchData fetchData, 187 @NonNull CustomAudienceUpdatableData updatableData) { 188 Objects.requireNonNull(fetchData); 189 Objects.requireNonNull(updatableData); 190 191 DBCustomAudience customAudience = 192 getCustomAudienceByPrimaryKey( 193 fetchData.getOwner(), fetchData.getBuyer(), fetchData.getName()); 194 195 if (customAudience == null) { 196 // This custom audience could have been cleaned up while it was being updated 197 return; 198 } 199 200 customAudience = customAudience.copyWithUpdatableData(updatableData); 201 202 persistCustomAudience(customAudience); 203 persistCustomAudienceBackgroundFetchData(fetchData); 204 List<ComponentAdData> componentAds = updatableData.getComponentAds(); 205 sLogger.v("Inserting Component Ads in the DB: %s", componentAds); 206 insertAndOverwriteComponentAds( 207 componentAds, 208 customAudience.getOwner(), 209 customAudience.getBuyer(), 210 customAudience.getName()); 211 } 212 213 /** Returns total size of the {@code custom_audience_quarantine} table. */ 214 @Query("SELECT COUNT(*) FROM custom_audience_quarantine") getTotalNumCustomAudienceQuarantineEntries()215 abstract long getTotalNumCustomAudienceQuarantineEntries(); 216 217 /** 218 * Safely inserts a {@link DBCustomAudienceQuarantine} into the table. 219 * 220 * @throws IllegalStateException if {@link #getTotalNumCustomAudienceQuarantineEntries} exceeds 221 * {@code maxEntries}. 222 * <p>This transaction is separate in order to minimize the critical region while locking 223 * the database. 224 */ 225 @Transaction safelyInsertCustomAudienceQuarantine( @onNull DBCustomAudienceQuarantine dbCustomAudienceQuarantine, long maxEntries)226 public void safelyInsertCustomAudienceQuarantine( 227 @NonNull DBCustomAudienceQuarantine dbCustomAudienceQuarantine, long maxEntries) 228 throws IllegalStateException { 229 Objects.requireNonNull(dbCustomAudienceQuarantine); 230 231 if (getTotalNumCustomAudienceQuarantineEntries() >= maxEntries) { 232 String errorMessage = 233 "Quarantine table maximum has been reached! Not persisting this entry"; 234 sLogger.e(errorMessage); 235 ErrorLogUtil.e( 236 AD_SERVICES_ERROR_REPORTED__ERROR_CODE__CUSTOM_AUDIENCE_DAO_QUARANTINE_TABLE_MAX_REACHED, 237 AD_SERVICES_ERROR_REPORTED__PPAPI_NAME__FLEDGE); 238 throw new IllegalStateException(errorMessage); 239 } 240 persistCustomAudienceQuarantineData(dbCustomAudienceQuarantine); 241 } 242 243 /** Get count of custom audience. */ 244 @Query("SELECT COUNT(*) FROM custom_audience") getCustomAudienceCount()245 public abstract long getCustomAudienceCount(); 246 247 /** Get count of custom audience of a given owner. */ 248 @Query("SELECT COUNT(*) FROM custom_audience WHERE owner=:owner") getCustomAudienceCountForOwner(String owner)249 public abstract long getCustomAudienceCountForOwner(String owner); 250 251 /** Get count of custom audience of a given buyer. */ 252 @Query("SELECT COUNT(*) FROM custom_audience WHERE buyer=:buyer") getCustomAudienceCountForBuyer(AdTechIdentifier buyer)253 public abstract long getCustomAudienceCountForBuyer(AdTechIdentifier buyer); 254 255 /** Get the total number of distinct custom audience owner. */ 256 @Query("SELECT COUNT(DISTINCT owner) FROM custom_audience") getCustomAudienceOwnerCount()257 public abstract long getCustomAudienceOwnerCount(); 258 259 /** List all custom audiences by owner and buyer that are marked as debuggable. */ 260 @Query( 261 "SELECT * FROM custom_audience " 262 + "WHERE owner=:owner AND buyer=:buyer AND debuggable=1") 263 @Nullable listDebuggableCustomAudiencesByOwnerAndBuyer( @onNull String owner, @NonNull AdTechIdentifier buyer)264 public abstract List<DBCustomAudience> listDebuggableCustomAudiencesByOwnerAndBuyer( 265 @NonNull String owner, @NonNull AdTechIdentifier buyer); 266 267 /** Get custom audience by owner, buyer and name that are marked as debuggable. */ 268 @Query( 269 "SELECT * FROM custom_audience " 270 + "WHERE owner = :owner AND buyer = :buyer AND name = :name AND debuggable = 1") getDebuggableCustomAudienceByPrimaryKey( @onNull String owner, @NonNull AdTechIdentifier buyer, @NonNull String name)271 public abstract DBCustomAudience getDebuggableCustomAudienceByPrimaryKey( 272 @NonNull String owner, @NonNull AdTechIdentifier buyer, @NonNull String name); 273 274 /** 275 * Get the count of total custom audience, the count for the given owner and the count of 276 * distinct owner in one transaction. 277 * 278 * @param owner the owner we need check the count against. 279 * @return the aggregated data of custom audience count 280 */ 281 @Transaction 282 @NonNull getCustomAudienceStats( @onNull String owner, @NonNull AdTechIdentifier buyer)283 public CustomAudienceStats getCustomAudienceStats( 284 @NonNull String owner, @NonNull AdTechIdentifier buyer) { 285 Objects.requireNonNull(owner, "Owner must not be null"); 286 Objects.requireNonNull(buyer, "Buyer must not be null"); 287 288 long customAudienceCount = getCustomAudienceCount(); 289 long customAudienceCountPerOwner = getCustomAudienceCountForOwner(owner); 290 long ownerCount = getCustomAudienceOwnerCount(); 291 long customAudienceCountPerBuyer = getCustomAudienceCountForBuyer(buyer); 292 293 // TODO(b/255780705): Add buyer and per-buyer stats 294 return CustomAudienceStats.builder() 295 .setOwner(owner) 296 .setBuyer(buyer) 297 .setTotalCustomAudienceCount(customAudienceCount) 298 .setPerOwnerCustomAudienceCount(customAudienceCountPerOwner) 299 .setTotalOwnerCount(ownerCount) 300 .setPerBuyerCustomAudienceCount(customAudienceCountPerBuyer) 301 .build(); 302 } 303 304 /** 305 * Add a custom audience override into the table custom_audience_overrides 306 * 307 * @param customAudienceOverride is the CustomAudienceOverride to add to table 308 * custom_audience_overrides. If a {@link DBCustomAudienceOverride} object with the primary 309 * key already exists, this will replace the existing object. 310 */ 311 @Insert(onConflict = OnConflictStrategy.REPLACE) persistCustomAudienceOverride( DBCustomAudienceOverride customAudienceOverride)312 public abstract void persistCustomAudienceOverride( 313 DBCustomAudienceOverride customAudienceOverride); 314 315 /** 316 * Checks if there is a row in the custom audience override data with the unique key combination 317 * of owner, buyer, and name 318 * 319 * @return true if row exists, false otherwise 320 */ 321 @Query( 322 "SELECT EXISTS(SELECT 1 FROM custom_audience_overrides WHERE owner = :owner " 323 + "AND buyer = :buyer AND name = :name LIMIT 1)") doesCustomAudienceOverrideExist( @onNull String owner, @NonNull AdTechIdentifier buyer, @NonNull String name)324 public abstract boolean doesCustomAudienceOverrideExist( 325 @NonNull String owner, @NonNull AdTechIdentifier buyer, @NonNull String name); 326 327 /** 328 * Checks if there is a row in the {@code custom_audience_quarantine} with the unique key 329 * combination of owner and buyer. 330 * 331 * @return true if row exists, false otherwise 332 */ 333 @Query( 334 "SELECT EXISTS(SELECT 1 FROM custom_audience_quarantine WHERE owner = :owner " 335 + "AND buyer = :buyer LIMIT 1)") doesCustomAudienceQuarantineExist( @onNull String owner, @NonNull AdTechIdentifier buyer)336 public abstract boolean doesCustomAudienceQuarantineExist( 337 @NonNull String owner, @NonNull AdTechIdentifier buyer); 338 339 /** 340 * Gets expiration time if it exists with the unique key combination of owner and buyer. Returns 341 * null otherwise. 342 */ 343 @Nullable 344 @Query( 345 "SELECT quarantine_expiration_time FROM custom_audience_quarantine WHERE owner = :owner" 346 + " AND buyer = :buyer") getCustomAudienceQuarantineExpiration( @onNull String owner, @NonNull AdTechIdentifier buyer)347 public abstract Instant getCustomAudienceQuarantineExpiration( 348 @NonNull String owner, @NonNull AdTechIdentifier buyer); 349 350 /** 351 * Get custom audience by its unique key. 352 * 353 * @return custom audience result if exists. 354 */ 355 @Query("SELECT * FROM custom_audience WHERE owner = :owner AND buyer = :buyer AND name = :name") 356 @Nullable getCustomAudienceByPrimaryKey( @onNull String owner, @NonNull AdTechIdentifier buyer, @NonNull String name)357 public abstract DBCustomAudience getCustomAudienceByPrimaryKey( 358 @NonNull String owner, @NonNull AdTechIdentifier buyer, @NonNull String name); 359 360 /** 361 * Get custom audiences by buyer and name. 362 * 363 * @return custom audiences result if exists. 364 */ 365 @Query("SELECT * FROM custom_audience WHERE buyer = :buyer AND name = :name") 366 @NonNull getCustomAudiencesForBuyerAndName( @onNull AdTechIdentifier buyer, @NonNull String name)367 public abstract List<DBCustomAudience> getCustomAudiencesForBuyerAndName( 368 @NonNull AdTechIdentifier buyer, @NonNull String name); 369 370 /** 371 * Get custom audience background fetch data by its unique key. 372 * 373 * @return custom audience background fetch data if it exists 374 */ 375 @Query( 376 "SELECT * FROM custom_audience_background_fetch_data " 377 + "WHERE owner = :owner AND buyer = :buyer AND name = :name") 378 @Nullable 379 @VisibleForTesting 380 public abstract DBCustomAudienceBackgroundFetchData getCustomAudienceBackgroundFetchDataByPrimaryKey( @onNull String owner, @NonNull AdTechIdentifier buyer, @NonNull String name)381 getCustomAudienceBackgroundFetchDataByPrimaryKey( 382 @NonNull String owner, @NonNull AdTechIdentifier buyer, @NonNull String name); 383 384 /** 385 * Get debuggable custom audience background fetch data by its unique key. 386 * 387 * @return custom audience background fetch data if it exists 388 */ 389 @Query( 390 "SELECT * FROM custom_audience_background_fetch_data WHERE owner = :owner AND buyer =" 391 + " :buyer AND name = :name AND is_debuggable = 1") 392 @Nullable 393 public abstract DBCustomAudienceBackgroundFetchData getDebuggableCustomAudienceBackgroundFetchDataByPrimaryKey( @onNull String owner, @NonNull AdTechIdentifier buyer, @NonNull String name)394 getDebuggableCustomAudienceBackgroundFetchDataByPrimaryKey( 395 @NonNull String owner, @NonNull AdTechIdentifier buyer, @NonNull String name); 396 397 /** 398 * List debuggable custom audience background fetch data by its unique key. 399 * 400 * @return custom audience background fetch data if it exists 401 */ 402 @Query( 403 "SELECT * FROM custom_audience_background_fetch_data " 404 + "WHERE owner = :owner AND buyer = :buyer AND is_debuggable = 1") 405 @Nullable 406 public abstract List<DBCustomAudienceBackgroundFetchData> listDebuggableCustomAudienceBackgroundFetchData( @onNull String owner, @NonNull AdTechIdentifier buyer)407 listDebuggableCustomAudienceBackgroundFetchData( 408 @NonNull String owner, @NonNull AdTechIdentifier buyer); 409 410 /** 411 * Get custom audience JS override by its unique key. 412 * 413 * <p>This method is not intended to be called on its own. Please use {@link 414 * #getBiddingLogicUriOverride(String, AdTechIdentifier, String, String)} instead. 415 * 416 * @return custom audience override result if exists. 417 */ 418 @Query( 419 "SELECT bidding_logic as bidding_logic_js, bidding_logic_version as" 420 + " buyer_bidding_logic_version FROM custom_audience_overrides WHERE owner =" 421 + " :owner AND buyer = :buyer AND name = :name AND app_package_name=" 422 + " :appPackageName") 423 @Nullable getBiddingLogicUriOverrideInternal( @onNull String owner, @NonNull AdTechIdentifier buyer, @NonNull String name, @NonNull String appPackageName)424 protected abstract BiddingLogicJsWithVersion getBiddingLogicUriOverrideInternal( 425 @NonNull String owner, 426 @NonNull AdTechIdentifier buyer, 427 @NonNull String name, 428 @NonNull String appPackageName); 429 430 /** 431 * Get custom audience JS override by its unique key. 432 * 433 * @return custom audience override result if exists. 434 */ getBiddingLogicUriOverride( @onNull String owner, @NonNull AdTechIdentifier buyer, @NonNull String name, @NonNull String appPackageName)435 public DecisionLogic getBiddingLogicUriOverride( 436 @NonNull String owner, 437 @NonNull AdTechIdentifier buyer, 438 @NonNull String name, 439 @NonNull String appPackageName) { 440 BiddingLogicJsWithVersion biddingLogicJsWithVersion = 441 getBiddingLogicUriOverrideInternal(owner, buyer, name, appPackageName); 442 443 if (Objects.isNull(biddingLogicJsWithVersion)) { 444 return null; 445 } 446 447 ImmutableMap.Builder<Integer, Long> versionMap = new ImmutableMap.Builder<>(); 448 if (Objects.nonNull(biddingLogicJsWithVersion.getBuyerBiddingLogicVersion())) { 449 versionMap.put( 450 JsVersionHelper.JS_PAYLOAD_TYPE_BUYER_BIDDING_LOGIC_JS, 451 biddingLogicJsWithVersion.getBuyerBiddingLogicVersion()); 452 } 453 454 return DecisionLogic.create( 455 biddingLogicJsWithVersion.getBiddingLogicJs(), versionMap.build()); 456 } 457 458 /** 459 * Get trusted bidding data override by its unique key. 460 * 461 * @return custom audience override result if exists. 462 */ 463 @Query( 464 "SELECT trusted_bidding_data FROM custom_audience_overrides WHERE owner = :owner " 465 + "AND buyer = :buyer AND name = :name AND app_package_name= :appPackageName") 466 @Nullable getTrustedBiddingDataOverride( @onNull String owner, @NonNull AdTechIdentifier buyer, @NonNull String name, @NonNull String appPackageName)467 public abstract String getTrustedBiddingDataOverride( 468 @NonNull String owner, 469 @NonNull AdTechIdentifier buyer, 470 @NonNull String name, 471 @NonNull String appPackageName); 472 473 /** Delete the custom audience given owner, buyer, and name. */ 474 @Query("DELETE FROM custom_audience WHERE owner = :owner AND buyer = :buyer AND name = :name") deleteCustomAudienceByPrimaryKey( @onNull String owner, @NonNull AdTechIdentifier buyer, @NonNull String name)475 protected abstract void deleteCustomAudienceByPrimaryKey( 476 @NonNull String owner, @NonNull AdTechIdentifier buyer, @NonNull String name); 477 478 /** Delete background fetch data for the custom audience given owner, buyer, and name. */ 479 @Query( 480 "DELETE FROM custom_audience_background_fetch_data WHERE owner = :owner " 481 + "AND buyer = :buyer AND name = :name") deleteCustomAudienceBackgroundFetchDataByPrimaryKey( @onNull String owner, @NonNull AdTechIdentifier buyer, @NonNull String name)482 protected abstract void deleteCustomAudienceBackgroundFetchDataByPrimaryKey( 483 @NonNull String owner, @NonNull AdTechIdentifier buyer, @NonNull String name); 484 485 /** 486 * Delete all custom audience data corresponding to the given {@code owner}, {@code buyer}, and 487 * {@code name} in a single transaction. 488 */ 489 @Transaction deleteAllCustomAudienceDataByPrimaryKey( @onNull String owner, @NonNull AdTechIdentifier buyer, @NonNull String name)490 public void deleteAllCustomAudienceDataByPrimaryKey( 491 @NonNull String owner, @NonNull AdTechIdentifier buyer, @NonNull String name) { 492 deleteCustomAudienceByPrimaryKey(owner, buyer, name); 493 deleteCustomAudienceBackgroundFetchDataByPrimaryKey(owner, buyer, name); 494 } 495 496 /** 497 * Deletes all custom audiences which are expired, where the custom audiences' expiration times 498 * match or precede the given {@code expiryTime}. 499 * 500 * <p>This method is not intended to be called on its own. Please use {@link 501 * #deleteAllExpiredCustomAudienceData(Instant)} instead. 502 * 503 * @return the number of deleted custom audiences 504 */ 505 @Query("DELETE FROM custom_audience WHERE expiration_time <= :expiryTime") deleteAllExpiredCustomAudiences(@onNull Instant expiryTime)506 protected abstract int deleteAllExpiredCustomAudiences(@NonNull Instant expiryTime); 507 508 /** 509 * Deletes all expired entries of the {@code custom_audience_quarantine} table. 510 * 511 * @return the number of deleted entries 512 */ 513 @Query( 514 "DELETE FROM custom_audience_quarantine WHERE quarantine_expiration_time <=" 515 + " :expiryTime") deleteAllExpiredQuarantineEntries(@onNull Instant expiryTime)516 public abstract int deleteAllExpiredQuarantineEntries(@NonNull Instant expiryTime); 517 518 /** 519 * Deletes all entries with the unique combination of owner and buyer. 520 * 521 * @return the number of deleted entries 522 */ 523 @Query("DELETE FROM custom_audience_quarantine WHERE owner = :owner " + "AND buyer = :buyer") deleteQuarantineEntry( @onNull String owner, @NonNull AdTechIdentifier buyer)524 public abstract int deleteQuarantineEntry( 525 @NonNull String owner, @NonNull AdTechIdentifier buyer); 526 527 /** 528 * Deletes background fetch data for all custom audiences which are expired, where the custom 529 * audiences' expiration times match or precede the given {@code expiryTime}. 530 * 531 * <p>This method is not intended to be called on its own. Please use {@link 532 * #deleteAllExpiredCustomAudienceData(Instant)} instead. 533 */ 534 @Query( 535 "DELETE FROM custom_audience_background_fetch_data WHERE ROWID IN " 536 + "(SELECT bgf.ROWID FROM custom_audience_background_fetch_data AS bgf " 537 + "INNER JOIN custom_audience AS ca " 538 + "ON bgf.buyer = ca.buyer AND bgf.owner = ca.owner AND bgf.name = ca.name " 539 + "WHERE expiration_time <= :expiryTime)") deleteAllExpiredCustomAudienceBackgroundFetchData( @onNull Instant expiryTime)540 protected abstract void deleteAllExpiredCustomAudienceBackgroundFetchData( 541 @NonNull Instant expiryTime); 542 543 /** 544 * Deletes all expired custom audience data in a single transaction, where the custom audiences' 545 * expiration times match or precede the given {@code expiryTime}. 546 * 547 * @return the number of deleted custom audiences 548 */ 549 @Transaction deleteAllExpiredCustomAudienceData(@onNull Instant expiryTime)550 public int deleteAllExpiredCustomAudienceData(@NonNull Instant expiryTime) { 551 deleteAllExpiredCustomAudienceBackgroundFetchData(expiryTime); 552 return deleteAllExpiredCustomAudiences(expiryTime); 553 } 554 555 /** Returns the set of all unique owner apps in the custom audience table. */ 556 @Query("SELECT DISTINCT owner FROM custom_audience") getAllCustomAudienceOwners()557 public abstract List<String> getAllCustomAudienceOwners(); 558 559 /** 560 * Deletes all custom audiences belonging to any app in the given set of {@code ownersToRemove}. 561 * 562 * <p>This method is not intended to be called on its own. Please use {@link 563 * #deleteAllDisallowedOwnerCustomAudienceData(PackageManager, Flags)} instead. 564 * 565 * @return the number of deleted custom audiences 566 */ 567 @Query("DELETE FROM custom_audience WHERE owner IN (:ownersToRemove)") deleteCustomAudiencesByOwner(@onNull List<String> ownersToRemove)568 protected abstract int deleteCustomAudiencesByOwner(@NonNull List<String> ownersToRemove); 569 570 /** 571 * Deletes all custom audience background fetch data belonging to any app in the given set of 572 * {@code ownersToRemove}. 573 * 574 * <p>This method is not intended to be called on its own. Please use {@link 575 * #deleteAllDisallowedOwnerCustomAudienceData(PackageManager, Flags)} instead. 576 */ 577 @Query("DELETE FROM custom_audience_background_fetch_data WHERE owner IN (:ownersToRemove)") deleteCustomAudienceBackgroundFetchDataByOwner( @onNull List<String> ownersToRemove)578 protected abstract void deleteCustomAudienceBackgroundFetchDataByOwner( 579 @NonNull List<String> ownersToRemove); 580 581 /** 582 * Deletes all custom audience data belonging to disallowed owner apps in a single transaction, 583 * where the custom audiences' owner apps cannot be found in the installed list or where the 584 * owner apps are not found in the app allowlist. 585 * 586 * @return a {@link CustomAudienceStats} object containing only the number of deleted custom 587 * audiences and the number of disallowed owner apps found 588 */ 589 @Transaction 590 @NonNull deleteAllDisallowedOwnerCustomAudienceData( @onNull PackageManager packageManager, @NonNull Flags flags)591 public CustomAudienceStats deleteAllDisallowedOwnerCustomAudienceData( 592 @NonNull PackageManager packageManager, @NonNull Flags flags) { 593 Objects.requireNonNull(packageManager); 594 Objects.requireNonNull(flags); 595 List<String> ownersToRemove = getAllCustomAudienceOwners(); 596 597 CleanupUtils.removeAllowedPackages( 598 ownersToRemove, packageManager, Arrays.asList(flags.getPpapiAppAllowList())); 599 600 long numDisallowedOwnersFound = ownersToRemove.size(); 601 long numRemovedCustomAudiences = 0; 602 if (!ownersToRemove.isEmpty()) { 603 deleteCustomAudienceBackgroundFetchDataByOwner(ownersToRemove); 604 numRemovedCustomAudiences = deleteCustomAudiencesByOwner(ownersToRemove); 605 } 606 607 return CustomAudienceStats.builder() 608 .setTotalCustomAudienceCount(numRemovedCustomAudiences) 609 .setTotalOwnerCount(numDisallowedOwnersFound) 610 .build(); 611 } 612 613 /** Returns the set of all unique buyer ad techs in the custom audience table. */ 614 @Query("SELECT DISTINCT buyer FROM custom_audience") getAllCustomAudienceBuyers()615 public abstract List<AdTechIdentifier> getAllCustomAudienceBuyers(); 616 617 /** 618 * Deletes all custom audiences belonging to any ad tech in the given set of {@code 619 * buyersToRemove}. 620 * 621 * <p>This method is not intended to be called on its own. Please use {@link 622 * #deleteAllDisallowedBuyerCustomAudienceData(EnrollmentDao, Flags)} instead. 623 * 624 * @return the number of deleted custom audiences 625 */ 626 @Query("DELETE FROM custom_audience WHERE buyer IN (:buyersToRemove)") deleteCustomAudiencesByBuyer( @onNull List<AdTechIdentifier> buyersToRemove)627 protected abstract int deleteCustomAudiencesByBuyer( 628 @NonNull List<AdTechIdentifier> buyersToRemove); 629 630 /** 631 * Deletes all custom audience background fetch data belonging to any ad tech in the given set 632 * of {@code buyersToRemove}. 633 * 634 * <p>This method is not intended to be called on its own. Please use {@link 635 * #deleteAllDisallowedBuyerCustomAudienceData(EnrollmentDao, Flags)} instead. 636 */ 637 @Query("DELETE FROM custom_audience_background_fetch_data WHERE buyer IN (:buyersToRemove)") deleteCustomAudienceBackgroundFetchDataByBuyer( @onNull List<AdTechIdentifier> buyersToRemove)638 protected abstract void deleteCustomAudienceBackgroundFetchDataByBuyer( 639 @NonNull List<AdTechIdentifier> buyersToRemove); 640 641 /** 642 * Deletes all custom audience data belonging to disallowed buyer ad techs in a single 643 * transaction, where the custom audiences' buyer ad techs cannot be found in the enrollment 644 * database. 645 * 646 * @return a {@link CustomAudienceStats} object containing only the number of deleted custom 647 * audiences and the number of disallowed owner apps found 648 */ 649 @Transaction 650 @NonNull deleteAllDisallowedBuyerCustomAudienceData( @onNull EnrollmentDao enrollmentDao, @NonNull Flags flags)651 public CustomAudienceStats deleteAllDisallowedBuyerCustomAudienceData( 652 @NonNull EnrollmentDao enrollmentDao, @NonNull Flags flags) { 653 Objects.requireNonNull(enrollmentDao); 654 Objects.requireNonNull(flags); 655 656 if (flags.getDisableFledgeEnrollmentCheck()) { 657 sLogger.d("FLEDGE enrollment check disabled; skipping enrolled buyer cleanup"); 658 return CustomAudienceStats.builder() 659 .setTotalCustomAudienceCount(0) 660 .setTotalBuyerCount(0) 661 .build(); 662 } 663 664 List<AdTechIdentifier> buyersToRemove = getAllCustomAudienceBuyers(); 665 666 if (!buyersToRemove.isEmpty()) { 667 Set<AdTechIdentifier> allowedAdTechs = enrollmentDao.getAllFledgeEnrolledAdTechs(); 668 buyersToRemove.removeAll(allowedAdTechs); 669 } 670 671 long numDisallowedBuyersFound = buyersToRemove.size(); 672 long numRemovedCustomAudiences = 0; 673 if (!buyersToRemove.isEmpty()) { 674 deleteCustomAudienceBackgroundFetchDataByBuyer(buyersToRemove); 675 numRemovedCustomAudiences = deleteCustomAudiencesByBuyer(buyersToRemove); 676 } 677 678 return CustomAudienceStats.builder() 679 .setTotalCustomAudienceCount(numRemovedCustomAudiences) 680 .setTotalBuyerCount(numDisallowedBuyersFound) 681 .build(); 682 } 683 684 /** 685 * Deletes ALL custom audiences from the table. 686 * 687 * <p>This method is not intended to be called on its own. Please use {@link 688 * #deleteAllCustomAudienceData(boolean)} instead. 689 */ 690 @Query("DELETE FROM custom_audience") deleteAllCustomAudiences()691 protected abstract void deleteAllCustomAudiences(); 692 693 /** 694 * Deletes ALL custom audience background fetch data from the table. 695 * 696 * <p>This method is not intended to be called on its own. Please use {@link 697 * #deleteAllCustomAudienceData(boolean)} instead. 698 */ 699 @Query("DELETE FROM custom_audience_background_fetch_data") deleteAllCustomAudienceBackgroundFetchData()700 protected abstract void deleteAllCustomAudienceBackgroundFetchData(); 701 702 /** 703 * Deletes ALL custom audience overrides from the table. 704 * 705 * <p>This method is not intended to be called on its own. Please use {@link 706 * #deleteAllCustomAudienceData(boolean)} instead. 707 */ 708 @Query("DELETE FROM custom_audience_overrides") deleteAllCustomAudienceOverrides()709 protected abstract void deleteAllCustomAudienceOverrides(); 710 711 /** Deletes ALL custom audience data from the database in a single transaction. */ 712 @Transaction deleteAllCustomAudienceData(boolean scheduleCustomAudienceEnabled)713 public void deleteAllCustomAudienceData(boolean scheduleCustomAudienceEnabled) { 714 deleteAllCustomAudiences(); 715 deleteAllCustomAudienceBackgroundFetchData(); 716 deleteAllCustomAudienceOverrides(); 717 if (scheduleCustomAudienceEnabled) { 718 deleteAllScheduledCustomAudienceUpdates(); 719 } 720 } 721 722 /** 723 * Deletes all custom audiences belonging to the {@code owner} application from the table. 724 * 725 * <p>This method is not intended to be called on its own. Please use {@link 726 * #deleteCustomAudienceDataByOwner(String, boolean)} instead. 727 */ 728 @Query("DELETE FROM custom_audience WHERE owner = :owner") deleteCustomAudiencesByOwner(@onNull String owner)729 protected abstract void deleteCustomAudiencesByOwner(@NonNull String owner); 730 731 /** 732 * Deletes all custom audience background fetch data belonging to the {@code owner} application 733 * from the table. 734 * 735 * <p>This method is not intended to be called on its own. Please use {@link 736 * #deleteCustomAudienceDataByOwner(String, boolean)} instead. 737 */ 738 @Query("DELETE FROM custom_audience_background_fetch_data WHERE owner = :owner") deleteCustomAudienceBackgroundFetchDataByOwner(@onNull String owner)739 protected abstract void deleteCustomAudienceBackgroundFetchDataByOwner(@NonNull String owner); 740 741 /** 742 * Deletes all custom audience overrides belonging to the {@code owner} application from the 743 * table. 744 * 745 * <p>This method is not intended to be called on its own. Please use {@link 746 * #deleteCustomAudienceDataByOwner(String, boolean)} instead. 747 */ 748 @Query("DELETE FROM custom_audience_overrides WHERE owner = :owner") deleteCustomAudienceOverridesByOwner(@onNull String owner)749 protected abstract void deleteCustomAudienceOverridesByOwner(@NonNull String owner); 750 751 /** 752 * Deletes all custom audience data belonging to the {@code owner} application from the database 753 * in a single transaction. 754 */ 755 @Transaction deleteCustomAudienceDataByOwner( @onNull String owner, boolean scheduleCustomAudienceEnabled)756 public void deleteCustomAudienceDataByOwner( 757 @NonNull String owner, boolean scheduleCustomAudienceEnabled) { 758 deleteCustomAudiencesByOwner(owner); 759 deleteCustomAudienceBackgroundFetchDataByOwner(owner); 760 deleteCustomAudienceOverridesByOwner(owner); 761 if (scheduleCustomAudienceEnabled) { 762 deleteScheduledCustomAudienceUpdatesByOwner(owner); 763 } 764 } 765 766 /** Clean up selected custom audience override data by its primary key */ 767 @Query( 768 "DELETE FROM custom_audience_overrides WHERE owner = :owner AND buyer = :buyer " 769 + "AND name = :name AND app_package_name = :appPackageName") removeCustomAudienceOverrideByPrimaryKeyAndPackageName( @onNull String owner, @NonNull AdTechIdentifier buyer, @NonNull String name, @NonNull String appPackageName)770 public abstract void removeCustomAudienceOverrideByPrimaryKeyAndPackageName( 771 @NonNull String owner, 772 @NonNull AdTechIdentifier buyer, 773 @NonNull String name, 774 @NonNull String appPackageName); 775 776 /** Clean up all custom audience override data for the given package name. */ 777 @Query("DELETE FROM custom_audience_overrides WHERE app_package_name = :appPackageName") removeCustomAudienceOverridesByPackageName(@onNull String appPackageName)778 public abstract void removeCustomAudienceOverridesByPackageName(@NonNull String appPackageName); 779 780 /** 781 * Fetch all active Custom Audience including null user_bidding_signals 782 * 783 * @param currentTime to compare against CA time values and find an active CA 784 * @return All the Custom Audience that represent 785 */ 786 @Query( 787 "SELECT * FROM custom_audience WHERE activation_time <= (:currentTime) AND" 788 + " (:currentTime) < expiration_time AND" 789 + " (last_ads_and_bidding_data_updated_time + (:activeWindowTimeMs)) >=" 790 + " (:currentTime) AND trusted_bidding_data_uri IS NOT NULL AND ads IS" 791 + " NOT NULL ") 792 @Nullable getAllActiveCustomAudienceForServerSideAuction( Instant currentTime, long activeWindowTimeMs)793 public abstract List<DBCustomAudience> getAllActiveCustomAudienceForServerSideAuction( 794 Instant currentTime, long activeWindowTimeMs); 795 796 /** 797 * Fetch all the Custom Audience corresponding to the buyers 798 * 799 * @param buyers associated with the Custom Audience 800 * @param currentTime to compare against CA time values and find an active CA 801 * @return All the Custom Audience that represent given buyers 802 */ 803 @Query( 804 "SELECT * FROM custom_audience WHERE buyer in (:buyers) AND activation_time <=" 805 + " (:currentTime) AND (:currentTime) < expiration_time AND" 806 + " (last_ads_and_bidding_data_updated_time + (:activeWindowTimeMs)) >=" 807 + " (:currentTime) AND user_bidding_signals IS NOT NULL AND" 808 + " trusted_bidding_data_uri IS NOT NULL AND ads IS NOT NULL ") 809 @Nullable getActiveCustomAudienceByBuyers( List<AdTechIdentifier> buyers, Instant currentTime, long activeWindowTimeMs)810 public abstract List<DBCustomAudience> getActiveCustomAudienceByBuyers( 811 List<AdTechIdentifier> buyers, Instant currentTime, long activeWindowTimeMs); 812 813 /** 814 * Gets up to {@code maxRowsReturned} rows of {@link DBCustomAudienceBackgroundFetchData} which 815 * correspond to custom audiences that are active, not expired, and eligible for update. 816 */ 817 @Query( 818 "SELECT bgf.* FROM custom_audience_background_fetch_data AS bgf " 819 + "INNER JOIN custom_audience AS ca " 820 + "ON bgf.buyer = ca.buyer AND bgf.owner = ca.owner AND bgf.name = ca.name " 821 + "WHERE bgf.eligible_update_time <= :currentTime " 822 + "AND ca.activation_time <= :currentTime " 823 + "AND :currentTime < ca.expiration_time " 824 + "ORDER BY ca.last_ads_and_bidding_data_updated_time ASC " 825 + "LIMIT :maxRowsReturned") 826 @NonNull 827 public abstract List<DBCustomAudienceBackgroundFetchData> getActiveEligibleCustomAudienceBackgroundFetchData( @onNull Instant currentTime, long maxRowsReturned)828 getActiveEligibleCustomAudienceBackgroundFetchData( 829 @NonNull Instant currentTime, long maxRowsReturned); 830 831 /** 832 * Gets the number of all {@link DBCustomAudienceBackgroundFetchData} for custom audiences that 833 * are active, not expired, and eligible for update. 834 */ 835 @Query( 836 "SELECT COUNT(DISTINCT bgf.ROWID) FROM custom_audience_background_fetch_data AS bgf " 837 + "INNER JOIN custom_audience AS ca " 838 + "ON bgf.buyer = ca.buyer AND bgf.owner = ca.owner AND bgf.name = ca.name " 839 + "WHERE bgf.eligible_update_time <= :currentTime " 840 + "AND ca.activation_time <= :currentTime " 841 + "AND :currentTime < ca.expiration_time") getNumActiveEligibleCustomAudienceBackgroundFetchData( @onNull Instant currentTime)842 public abstract int getNumActiveEligibleCustomAudienceBackgroundFetchData( 843 @NonNull Instant currentTime); 844 845 /** 846 * Persists a delayed Custom Audience Update along with the overrides and custom audiences to 847 * leave, if shouldReplacePendingUpdates is {@code true}, then we remove the pending updates 848 * with the same buyer and owner from the tables. If is {@code false} then we make sure that 849 * there are no ending updates and throw an {@link IllegalStateException} if there are pending 850 * updates with the same buyer and owner 851 * 852 * @param update delayed update 853 * @param partialCustomAudienceList overrides for incoming custom audiences 854 * @param customAudienceToLeaveList custom audiences to leave 855 */ 856 // TODO(b/324478492) Refactor Update queries in a separate Dao 857 @Transaction insertScheduledCustomAudienceUpdate( @onNull DBScheduledCustomAudienceUpdate update, @NonNull List<PartialCustomAudience> partialCustomAudienceList, @NonNull List<String> customAudienceToLeaveList, boolean shouldReplacePendingUpdates, ScheduledCustomAudienceUpdateScheduleAttemptedStats.Builder statsBuilder)858 public void insertScheduledCustomAudienceUpdate( 859 @NonNull DBScheduledCustomAudienceUpdate update, 860 @NonNull List<PartialCustomAudience> partialCustomAudienceList, 861 @NonNull List<String> customAudienceToLeaveList, 862 boolean shouldReplacePendingUpdates, 863 ScheduledCustomAudienceUpdateScheduleAttemptedStats.Builder statsBuilder) { 864 int pendingUpdates = 865 getNumberOfScheduleCAUpdatesByOwnerAndBuyer(update.getOwner(), update.getBuyer()); 866 if (pendingUpdates != 0) { 867 if (shouldReplacePendingUpdates) { 868 statsBuilder.setExistingUpdateStatus( 869 SCHEDULE_CA_UPDATE_EXISTING_UPDATE_STATUS_DID_OVERWRITE_EXISTING_UPDATE); 870 deleteScheduleCAUpdatesByOwnerAndBuyer(update.getOwner(), update.getBuyer()); 871 } else { 872 statsBuilder.setExistingUpdateStatus( 873 SCHEDULE_CA_UPDATE_EXISTING_UPDATE_STATUS_REJECTED_BY_EXISTING_UPDATE); 874 PersistScheduleCAUpdateException exception = 875 new PersistScheduleCAUpdateException( 876 String.format( 877 Locale.ENGLISH, 878 "Failed to persist scheduled update due to %d existing" 879 + " pending update(s)", 880 pendingUpdates)); 881 ErrorLogUtil.e( 882 exception, 883 AD_SERVICES_ERROR_REPORTED__ERROR_CODE__CUSTOM_AUDIENCE_DAO_FAILED_DUE_TO_PENDING_SCHEDULE, 884 AD_SERVICES_ERROR_REPORTED__PPAPI_NAME__SCHEDULE_CUSTOM_AUDIENCE_UPDATE); 885 throw exception; 886 } 887 } else { 888 statsBuilder.setExistingUpdateStatus( 889 SCHEDULE_CA_UPDATE_EXISTING_UPDATE_STATUS_NO_EXISTING_UPDATE); 890 } 891 892 long updateId = insertScheduledCustomAudienceUpdate(update); 893 894 if (!partialCustomAudienceList.isEmpty()) { 895 List<DBPartialCustomAudience> dbPartialCustomAudienceList = 896 partialCustomAudienceList.stream() 897 .map( 898 partialCa -> 899 DBPartialCustomAudience.fromPartialCustomAudience( 900 updateId, partialCa)) 901 .collect(Collectors.toList()); 902 903 insertPartialCustomAudiencesForUpdate(dbPartialCustomAudienceList); 904 } 905 906 if (!customAudienceToLeaveList.isEmpty()) { 907 List<DBCustomAudienceToLeave> dbLeaveList = 908 customAudienceToLeaveList.stream() 909 .map(caToLeave -> DBCustomAudienceToLeave.create(updateId, caToLeave)) 910 .collect(Collectors.toList()); 911 912 insertCustomAudiencesToLeaveForUpdate(dbLeaveList); 913 } 914 } 915 916 /** Gets updates schedule before a given time along with its corresponding overrides */ 917 @Transaction getScheduledCustomAudienceUpdateRequests( @onNull Instant timestamp)918 public List<DBScheduledCustomAudienceUpdateRequest> getScheduledCustomAudienceUpdateRequests( 919 @NonNull Instant timestamp) { 920 921 List<DBScheduledCustomAudienceUpdate> scheduledUpdates = 922 getCustomAudienceUpdatesScheduledBeforeTime(timestamp); 923 924 List<DBScheduledCustomAudienceUpdateRequest> updatesList = new ArrayList<>(); 925 scheduledUpdates.forEach( 926 update -> 927 updatesList.add( 928 DBScheduledCustomAudienceUpdateRequest.builder() 929 .setUpdate(update) 930 .setPartialCustomAudienceList( 931 getPartialAudienceListForUpdateId( 932 update.getUpdateId())) 933 .build())); 934 935 return updatesList; 936 } 937 938 /** 939 * Gets updates schedule before a given time along with its corresponding overrides and custom 940 * audiences to leave 941 */ 942 @Transaction 943 public List<DBScheduledCustomAudienceUpdateRequest> getScheduledCustomAudienceUpdateRequestsWithLeave(@onNull Instant timestamp)944 getScheduledCustomAudienceUpdateRequestsWithLeave(@NonNull Instant timestamp) { 945 946 List<DBScheduledCustomAudienceUpdate> scheduledUpdates = 947 getCustomAudienceUpdatesScheduledBeforeTime(timestamp); 948 949 List<DBScheduledCustomAudienceUpdateRequest> updatesList = new ArrayList<>(); 950 scheduledUpdates.forEach( 951 update -> 952 updatesList.add( 953 DBScheduledCustomAudienceUpdateRequest.builder() 954 .setUpdate(update) 955 .setPartialCustomAudienceList( 956 getPartialAudienceListForUpdateId( 957 update.getUpdateId())) 958 .setCustomAudienceToLeaveList( 959 getCustomAudienceToLeaveListForUpdateId( 960 update.getUpdateId())) 961 .build())); 962 963 return updatesList; 964 } 965 966 /** Persists a delayed Custom Audience Update and generated a unique update_id */ 967 @Insert(onConflict = OnConflictStrategy.REPLACE) insertScheduledCustomAudienceUpdate( @onNull DBScheduledCustomAudienceUpdate update)968 public abstract long insertScheduledCustomAudienceUpdate( 969 @NonNull DBScheduledCustomAudienceUpdate update); 970 971 /** Persists Custom Audience Overrides associated with a delayed update */ 972 @Insert(onConflict = OnConflictStrategy.REPLACE) insertPartialCustomAudiencesForUpdate( @onNull List<DBPartialCustomAudience> partialCustomAudienceList)973 abstract void insertPartialCustomAudiencesForUpdate( 974 @NonNull List<DBPartialCustomAudience> partialCustomAudienceList); 975 976 /** Gets Custom Audience Overrides associated with a delayed update */ 977 @Query("SELECT * FROM partial_custom_audience WHERE update_id = :updateId") getPartialAudienceListForUpdateId(Long updateId)978 abstract List<DBPartialCustomAudience> getPartialAudienceListForUpdateId(Long updateId); 979 980 /** Persists Custom Audiences to leave associated with a delayed update */ 981 @Insert(onConflict = OnConflictStrategy.REPLACE) insertCustomAudiencesToLeaveForUpdate( @onNull List<DBCustomAudienceToLeave> customAudienceToLeaveList)982 abstract void insertCustomAudiencesToLeaveForUpdate( 983 @NonNull List<DBCustomAudienceToLeave> customAudienceToLeaveList); 984 985 /** Gets Custom Audiences to leave associated with a delayed update */ 986 @Query("SELECT * FROM custom_audience_to_leave WHERE update_id = :updateId") getCustomAudienceToLeaveListForUpdateId(Long updateId)987 abstract List<DBCustomAudienceToLeave> getCustomAudienceToLeaveListForUpdateId(Long updateId); 988 989 /** Gets list of delayed Custom Audience Updates scheduled before the given time */ 990 @Query("SELECT * FROM scheduled_custom_audience_update WHERE scheduled_time <= :timestamp") 991 public abstract List<DBScheduledCustomAudienceUpdate> getCustomAudienceUpdatesScheduledBeforeTime(Instant timestamp)992 getCustomAudienceUpdatesScheduledBeforeTime(Instant timestamp); 993 994 /** Gets list of delayed Custom Audience Updates scheduled by owner */ 995 @Query("SELECT * FROM scheduled_custom_audience_update WHERE owner = :owner") getCustomAudienceUpdatesScheduledByOwner( String owner)996 public abstract List<DBScheduledCustomAudienceUpdate> getCustomAudienceUpdatesScheduledByOwner( 997 String owner); 998 999 /** Gets list of delayed Custom Audience Updates created before the given time */ 1000 @Query("DELETE FROM scheduled_custom_audience_update WHERE creation_time <= :timestamp") deleteScheduledCustomAudienceUpdatesCreatedBeforeTime(Instant timestamp)1001 public abstract void deleteScheduledCustomAudienceUpdatesCreatedBeforeTime(Instant timestamp); 1002 1003 /** Removes all the Custom Audience Update with the given owner */ 1004 @Query("DELETE FROM scheduled_custom_audience_update WHERE owner = :owner") deleteScheduledCustomAudienceUpdatesByOwner(String owner)1005 abstract void deleteScheduledCustomAudienceUpdatesByOwner(String owner); 1006 1007 /** Deletes all the Custom Audience Updates which matches the given owner and buyer */ 1008 @Query("DELETE FROM scheduled_custom_audience_update where owner = :owner AND buyer = :buyer") deleteScheduleCAUpdatesByOwnerAndBuyer(String owner, AdTechIdentifier buyer)1009 abstract void deleteScheduleCAUpdatesByOwnerAndBuyer(String owner, AdTechIdentifier buyer); 1010 1011 /** Returns the number of Custom Audience Updates with matching owner and buyer */ 1012 @Query( 1013 "SELECT COUNT(*) FROM scheduled_custom_audience_update where owner = :owner AND buyer =" 1014 + " :buyer") getNumberOfScheduleCAUpdatesByOwnerAndBuyer(String owner, AdTechIdentifier buyer)1015 abstract int getNumberOfScheduleCAUpdatesByOwnerAndBuyer(String owner, AdTechIdentifier buyer); 1016 1017 /** 1018 * Deletes all the Custom Audience Updates data 1019 * 1020 * <p>This method is not intended to be called on its own. Please use {@link 1021 * #deleteAllCustomAudienceData(boolean)} instead. 1022 */ 1023 @Query("DELETE FROM scheduled_custom_audience_update") deleteAllScheduledCustomAudienceUpdates()1024 abstract void deleteAllScheduledCustomAudienceUpdates(); 1025 1026 /** 1027 * Removes a Custom Audience Update from storage and cascades the deletion to associated Partial 1028 * Custom Audiences for overrides 1029 */ 1030 @Delete deleteScheduledCustomAudienceUpdate( @onNull DBScheduledCustomAudienceUpdate update)1031 public abstract void deleteScheduledCustomAudienceUpdate( 1032 @NonNull DBScheduledCustomAudienceUpdate update); 1033 1034 /** Persists a component ad */ 1035 @Insert(onConflict = OnConflictStrategy.REPLACE) insertComponentAdData(DBComponentAdData componentAdData)1036 abstract void insertComponentAdData(DBComponentAdData componentAdData); 1037 1038 /** 1039 * Clears any existing component ads associated with the primary keys of this custom audience, 1040 * then inserts a list of component ads. 1041 */ 1042 @Transaction insertAndOverwriteComponentAds( List<ComponentAdData> componentAdDataList, String owner, AdTechIdentifier buyer, String name)1043 public void insertAndOverwriteComponentAds( 1044 List<ComponentAdData> componentAdDataList, 1045 String owner, 1046 AdTechIdentifier buyer, 1047 String name) { 1048 deleteComponentAdsByCustomAudienceInfo(owner, buyer, name); 1049 for (ComponentAdData componentAdData : componentAdDataList) { 1050 DBComponentAdData dbComponentAdData = 1051 DBComponentAdData.builder() 1052 .setRenderUri(componentAdData.getRenderUri()) 1053 .setRenderId(componentAdData.getAdRenderId()) 1054 .setOwner(owner) 1055 .setBuyer(buyer) 1056 .setName(name) 1057 .build(); 1058 insertComponentAdData(dbComponentAdData); 1059 } 1060 } 1061 1062 /** 1063 * Gets a list of component ads associated with the primary keys of a custom audience. This list 1064 * will be ordered by the ascending order in which the component ads were persisted. 1065 */ 1066 @Query( 1067 "SELECT * FROM component_ad_data WHERE owner = :owner AND buyer = :buyer AND name =" 1068 + " :name ORDER BY rowId") getComponentAdsByCustomAudienceInfo( String owner, AdTechIdentifier buyer, String name)1069 public abstract List<DBComponentAdData> getComponentAdsByCustomAudienceInfo( 1070 String owner, AdTechIdentifier buyer, String name); 1071 1072 /** 1073 * Gets all the component ads matching a set of buyers. The component ads will be sorted by the 1074 * order in which they were inserted. 1075 */ 1076 @Query("SELECT * FROM component_ad_data WHERE buyer in (:buyerSet)") getComponentAdsByBuyers(Set<AdTechIdentifier> buyerSet)1077 public abstract List<DBComponentAdData> getComponentAdsByBuyers(Set<AdTechIdentifier> buyerSet); 1078 1079 /** Deletes all component ads associated with the primary keys of a custom audience. */ 1080 @Query( 1081 "DELETE FROM component_ad_data WHERE owner = :owner AND buyer = :buyer AND name =" 1082 + " :name") deleteComponentAdsByCustomAudienceInfo( String owner, AdTechIdentifier buyer, String name)1083 abstract void deleteComponentAdsByCustomAudienceInfo( 1084 String owner, AdTechIdentifier buyer, String name); 1085 1086 @VisibleForTesting 1087 static class BiddingLogicJsWithVersion { 1088 @ColumnInfo(name = "bidding_logic_js") 1089 @NonNull 1090 String mBiddingLogicJs; 1091 1092 @ColumnInfo(name = "buyer_bidding_logic_version") 1093 @Nullable 1094 Long mBuyerBiddingLogicVersion; 1095 BiddingLogicJsWithVersion( @onNull String biddingLogicJs, @Nullable Long buyerBiddingLogicVersion)1096 BiddingLogicJsWithVersion( 1097 @NonNull String biddingLogicJs, @Nullable Long buyerBiddingLogicVersion) { 1098 this.mBiddingLogicJs = biddingLogicJs; 1099 this.mBuyerBiddingLogicVersion = buyerBiddingLogicVersion; 1100 } 1101 1102 @NonNull getBiddingLogicJs()1103 public String getBiddingLogicJs() { 1104 return mBiddingLogicJs; 1105 } 1106 1107 @Nullable getBuyerBiddingLogicVersion()1108 public Long getBuyerBiddingLogicVersion() { 1109 return mBuyerBiddingLogicVersion; 1110 } 1111 } 1112 } 1113