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 android.adservices.customaudience.CustomAudience.FLAG_AUCTION_SERVER_REQUEST_DEFAULT; 20 21 import android.adservices.common.AdSelectionSignals; 22 import android.adservices.common.AdTechIdentifier; 23 import android.adservices.customaudience.CustomAudience; 24 import android.net.Uri; 25 26 import androidx.annotation.NonNull; 27 import androidx.annotation.Nullable; 28 import androidx.room.ColumnInfo; 29 import androidx.room.Embedded; 30 import androidx.room.Entity; 31 import androidx.room.ProvidedTypeConverter; 32 import androidx.room.TypeConverter; 33 import androidx.room.TypeConverters; 34 35 import com.android.adservices.data.common.DBAdData; 36 import com.android.adservices.service.customaudience.CustomAudienceUpdatableData; 37 import com.android.adservices.service.profiling.Tracing; 38 import com.android.internal.util.Preconditions; 39 40 import org.json.JSONArray; 41 import org.json.JSONException; 42 import org.json.JSONObject; 43 44 import java.time.Duration; 45 import java.time.Instant; 46 import java.util.ArrayList; 47 import java.util.List; 48 import java.util.Objects; 49 import java.util.Optional; 50 import java.util.stream.Collectors; 51 52 /** 53 * POJO represents a Custom Audience Database Entity. TODO: Align on the class naming strategy. 54 * (b/228095626) 55 */ 56 @Entity( 57 tableName = DBCustomAudience.TABLE_NAME, 58 primaryKeys = {"owner", "buyer", "name"}) 59 @TypeConverters({DBCustomAudience.Converters.class}) 60 public class DBCustomAudience { 61 public static final String TABLE_NAME = "custom_audience"; 62 63 @ColumnInfo(name = "owner", index = true) 64 @NonNull 65 private final String mOwner; 66 67 @ColumnInfo(name = "buyer", index = true) 68 @NonNull 69 private final AdTechIdentifier mBuyer; 70 71 @ColumnInfo(name = "name") 72 @NonNull 73 private final String mName; 74 75 @ColumnInfo(name = "expiration_time", index = true) 76 @NonNull 77 private final Instant mExpirationTime; 78 79 // TODO(b/234429221): Investigate and decide if should add an index on the activation_time. 80 @ColumnInfo(name = "activation_time") 81 @NonNull 82 private final Instant mActivationTime; 83 84 @ColumnInfo(name = "creation_time") 85 @NonNull 86 private final Instant mCreationTime; 87 88 @ColumnInfo(name = "last_ads_and_bidding_data_updated_time", index = true) 89 @NonNull 90 private final Instant mLastAdsAndBiddingDataUpdatedTime; 91 92 @ColumnInfo(name = "user_bidding_signals") 93 @Nullable 94 private final AdSelectionSignals mUserBiddingSignals; 95 96 @Embedded(prefix = "trusted_bidding_data_") 97 @Nullable 98 private final DBTrustedBiddingData mTrustedBiddingData; 99 100 @ColumnInfo(name = "bidding_logic_uri") 101 @NonNull 102 private final Uri mBiddingLogicUri; 103 104 @ColumnInfo(name = "ads") 105 @Nullable 106 private final List<DBAdData> mAds; 107 108 @ColumnInfo(name = "debuggable", defaultValue = "0") 109 private final boolean mDebuggable; 110 111 @ColumnInfo(name = "auction_server_request_flags", defaultValue = "0") 112 @CustomAudience.AuctionServerRequestFlag 113 private final int mAuctionServerRequestFlags; 114 DBCustomAudience( @onNull String owner, @NonNull AdTechIdentifier buyer, @NonNull String name, @NonNull Instant expirationTime, @NonNull Instant activationTime, @NonNull Instant creationTime, @NonNull Instant lastAdsAndBiddingDataUpdatedTime, @Nullable AdSelectionSignals userBiddingSignals, @Nullable DBTrustedBiddingData trustedBiddingData, @NonNull Uri biddingLogicUri, @Nullable List<DBAdData> ads, boolean debuggable, @CustomAudience.AuctionServerRequestFlag int auctionServerRequestFlags)115 public DBCustomAudience( 116 @NonNull String owner, 117 @NonNull AdTechIdentifier buyer, 118 @NonNull String name, 119 @NonNull Instant expirationTime, 120 @NonNull Instant activationTime, 121 @NonNull Instant creationTime, 122 @NonNull Instant lastAdsAndBiddingDataUpdatedTime, 123 @Nullable AdSelectionSignals userBiddingSignals, 124 @Nullable DBTrustedBiddingData trustedBiddingData, 125 @NonNull Uri biddingLogicUri, 126 @Nullable List<DBAdData> ads, 127 boolean debuggable, 128 @CustomAudience.AuctionServerRequestFlag int auctionServerRequestFlags) { 129 Preconditions.checkStringNotEmpty(owner, "Owner must be provided"); 130 Objects.requireNonNull(buyer, "Buyer must be provided."); 131 Preconditions.checkStringNotEmpty(name, "Name must be provided"); 132 Objects.requireNonNull(expirationTime, "Expiration time must be provided."); 133 Objects.requireNonNull(creationTime, "Creation time must be provided."); 134 Objects.requireNonNull( 135 lastAdsAndBiddingDataUpdatedTime, 136 "Last ads and bidding data updated time must be provided."); 137 Objects.requireNonNull(biddingLogicUri, "Bidding logic uri must be provided."); 138 139 mOwner = owner; 140 mBuyer = buyer; 141 mName = name; 142 mExpirationTime = expirationTime; 143 mActivationTime = activationTime; 144 mCreationTime = creationTime; 145 mLastAdsAndBiddingDataUpdatedTime = lastAdsAndBiddingDataUpdatedTime; 146 mUserBiddingSignals = userBiddingSignals; 147 mTrustedBiddingData = trustedBiddingData; 148 mBiddingLogicUri = biddingLogicUri; 149 mAds = ads; 150 mDebuggable = debuggable; 151 mAuctionServerRequestFlags = auctionServerRequestFlags; 152 } 153 154 /** 155 * Parse parcelable {@link CustomAudience} to storage model {@link DBCustomAudience}. 156 * 157 * @param parcelable the service model 158 * @param callerPackageName the String package name for the calling application, used as the 159 * owner app identifier 160 * @param currentTime the timestamp when calling the method 161 * @param defaultExpireIn the default expiration from activation 162 * @param adDataConversionStrategy Strategy to convert ads from DB 163 * @param debuggable If the CA was created in a debuggable context 164 * @param auctionServerRequestFlagsEnabled If auction server request flags are enabled. 165 * @return storage model 166 */ 167 @NonNull fromServiceObject( @onNull CustomAudience parcelable, @NonNull String callerPackageName, @NonNull Instant currentTime, @NonNull Duration defaultExpireIn, @NonNull AdDataConversionStrategy adDataConversionStrategy, boolean debuggable, boolean auctionServerRequestFlagsEnabled)168 public static DBCustomAudience fromServiceObject( 169 @NonNull CustomAudience parcelable, 170 @NonNull String callerPackageName, 171 @NonNull Instant currentTime, 172 @NonNull Duration defaultExpireIn, 173 @NonNull AdDataConversionStrategy adDataConversionStrategy, 174 boolean debuggable, 175 boolean auctionServerRequestFlagsEnabled) { 176 Objects.requireNonNull(parcelable); 177 Objects.requireNonNull(callerPackageName); 178 Objects.requireNonNull(currentTime); 179 Objects.requireNonNull(defaultExpireIn); 180 Objects.requireNonNull(adDataConversionStrategy); 181 182 // Setting default value to be currentTime. 183 // Make it easier at query for activated CAs. 184 Instant activationTime = 185 Optional.ofNullable(parcelable.getActivationTime()).orElse(currentTime); 186 if (activationTime.isBefore(currentTime)) { 187 activationTime = currentTime; 188 } 189 190 Instant expirationTime = 191 Optional.ofNullable(parcelable.getExpirationTime()) 192 .orElse(activationTime.plus(defaultExpireIn)); 193 194 Instant lastAdsAndBiddingDataUpdatedTime = 195 parcelable.getAds().isEmpty() 196 || parcelable.getTrustedBiddingData() == null 197 || parcelable.getUserBiddingSignals() == null 198 ? Instant.EPOCH 199 : currentTime; 200 201 return new DBCustomAudience.Builder() 202 .setName(parcelable.getName()) 203 .setBuyer(parcelable.getBuyer()) 204 .setOwner(callerPackageName) 205 .setActivationTime(activationTime) 206 .setCreationTime(currentTime) 207 .setLastAdsAndBiddingDataUpdatedTime(lastAdsAndBiddingDataUpdatedTime) 208 .setExpirationTime(expirationTime) 209 .setBiddingLogicUri(parcelable.getBiddingLogicUri()) 210 .setTrustedBiddingData( 211 DBTrustedBiddingData.fromServiceObject(parcelable.getTrustedBiddingData())) 212 .setDebuggable(debuggable) 213 .setAds( 214 parcelable.getAds().isEmpty() 215 ? null 216 : parcelable.getAds().stream() 217 .map( 218 parcelableAd -> 219 adDataConversionStrategy 220 .fromServiceObject(parcelableAd) 221 .build()) 222 .collect(Collectors.toList())) 223 .setUserBiddingSignals(parcelable.getUserBiddingSignals()) 224 .setAuctionServerRequestFlags( 225 auctionServerRequestFlagsEnabled 226 ? parcelable.getAuctionServerRequestFlags() 227 : FLAG_AUCTION_SERVER_REQUEST_DEFAULT) 228 .build(); 229 } 230 231 // TODO(b/321092996) Update this once {@link CustomAudienceUpdatableData} is updated with the 232 // new field 233 /** 234 * Creates a copy of the current {@link DBCustomAudience} object updated with data from a {@link 235 * CustomAudienceUpdatableData} object. 236 */ 237 @NonNull copyWithUpdatableData( @onNull CustomAudienceUpdatableData updatableData)238 public DBCustomAudience copyWithUpdatableData( 239 @NonNull CustomAudienceUpdatableData updatableData) { 240 Objects.requireNonNull(updatableData); 241 242 if (!updatableData.getContainsSuccessfulUpdate()) { 243 return this; 244 } 245 246 DBCustomAudience.Builder customAudienceBuilder = 247 new Builder(this) 248 .setLastAdsAndBiddingDataUpdatedTime( 249 updatableData.getAttemptedUpdateTime()); 250 251 if (updatableData.getUserBiddingSignals() != null) { 252 customAudienceBuilder.setUserBiddingSignals(updatableData.getUserBiddingSignals()); 253 } 254 255 if (updatableData.getTrustedBiddingData() != null) { 256 customAudienceBuilder.setTrustedBiddingData(updatableData.getTrustedBiddingData()); 257 } 258 259 if (updatableData.getAds() != null) { 260 customAudienceBuilder.setAds(updatableData.getAds()); 261 } 262 263 return customAudienceBuilder.build(); 264 } 265 266 /** The package name of the App that adds the user to this custom audience. */ 267 @NonNull getOwner()268 public String getOwner() { 269 return mOwner; 270 } 271 272 /** 273 * The ad-tech who can read this custom audience information and return back relevant ad 274 * information. This is expected to be the domain’s name used in biddingLogicUri and 275 * dailyUpdateUri. 276 * 277 * <p>Max length: 200 bytes 278 */ 279 @NonNull getBuyer()280 public AdTechIdentifier getBuyer() { 281 return mBuyer; 282 } 283 284 /** 285 * Identifies the CustomAudience within the set of ones created for this combination of owner 286 * and buyer. 287 * 288 * <p>Max length: 200 bytes 289 */ 290 @NonNull getName()291 public String getName() { 292 return mName; 293 } 294 295 /** 296 * Defines until when the CA end to be effective, this can be used to remove a user from this CA 297 * only after a defined period of time. 298 * 299 * <p>Default to be 60 days after activation(Pending product confirm). 300 * 301 * <p>Should be within 1 year since activation. 302 */ 303 @NonNull getExpirationTime()304 public Instant getExpirationTime() { 305 return mExpirationTime; 306 } 307 308 /** 309 * Defines when the CA starts to be effective, this can be used to enroll a user to this CA only 310 * after a defined interval (for example to track the fact that the user has not been using the 311 * app in the last n days). 312 * 313 * <p>Should be within 1 year since creation. 314 */ 315 @NonNull getActivationTime()316 public Instant getActivationTime() { 317 return mActivationTime; 318 } 319 320 /** Returns the time the CA was created. */ 321 @NonNull getCreationTime()322 public Instant getCreationTime() { 323 return mCreationTime; 324 } 325 326 /** Returns the time the CA ads and bidding data was last updated. */ 327 @NonNull getLastAdsAndBiddingDataUpdatedTime()328 public Instant getLastAdsAndBiddingDataUpdatedTime() { 329 return mLastAdsAndBiddingDataUpdatedTime; 330 } 331 332 /** 333 * Signals needed for any on-device bidding for remarketing Ads. For instance, an App might 334 * decide to store an embedding from their user features a model here while creating the custom 335 * audience. 336 */ 337 @Nullable getUserBiddingSignals()338 public AdSelectionSignals getUserBiddingSignals() { 339 return mUserBiddingSignals; 340 } 341 342 /** 343 * An ad-tech can define what data needs to be fetched from a trusted server 344 * (trusted_bidding_keys) and where it should be fetched from (trusted_bidding_uri). 345 */ 346 @Nullable getTrustedBiddingData()347 public DBTrustedBiddingData getTrustedBiddingData() { 348 return mTrustedBiddingData; 349 } 350 351 /** Returns the URI to fetch bidding logic js. */ 352 @NonNull getBiddingLogicUri()353 public Uri getBiddingLogicUri() { 354 return mBiddingLogicUri; 355 } 356 357 /** Returns Ads metadata that used to render an ad. */ 358 @Nullable getAds()359 public List<DBAdData> getAds() { 360 return mAds; 361 } 362 363 /** Returns if CA was created in a debuggable context. */ isDebuggable()364 public boolean isDebuggable() { 365 return mDebuggable; 366 } 367 368 /** Returns the bitfield of auction server request flags. */ 369 @CustomAudience.AuctionServerRequestFlag getAuctionServerRequestFlags()370 public int getAuctionServerRequestFlags() { 371 return mAuctionServerRequestFlags; 372 } 373 374 @Override equals(Object o)375 public boolean equals(Object o) { 376 if (this == o) return true; 377 if (!(o instanceof DBCustomAudience)) return false; 378 DBCustomAudience that = (DBCustomAudience) o; 379 return mOwner.equals(that.mOwner) 380 && mBuyer.equals(that.mBuyer) 381 && mName.equals(that.mName) 382 && mExpirationTime.equals(that.mExpirationTime) 383 && mActivationTime.equals(that.mActivationTime) 384 && mCreationTime.equals(that.mCreationTime) 385 && mLastAdsAndBiddingDataUpdatedTime.equals(that.mLastAdsAndBiddingDataUpdatedTime) 386 && Objects.equals(mUserBiddingSignals, that.mUserBiddingSignals) 387 && Objects.equals(mTrustedBiddingData, that.mTrustedBiddingData) 388 && mBiddingLogicUri.equals(that.mBiddingLogicUri) 389 && Objects.equals(mAds, that.mAds) 390 && mAuctionServerRequestFlags == that.mAuctionServerRequestFlags; 391 } 392 393 @Override hashCode()394 public int hashCode() { 395 return Objects.hash( 396 mOwner, 397 mBuyer, 398 mName, 399 mExpirationTime, 400 mActivationTime, 401 mCreationTime, 402 mLastAdsAndBiddingDataUpdatedTime, 403 mUserBiddingSignals, 404 mTrustedBiddingData, 405 mBiddingLogicUri, 406 mAds, 407 mDebuggable, 408 mAuctionServerRequestFlags); 409 } 410 411 /** 412 * @return a new builder instance created from this object's cloned data 413 * @hide 414 */ 415 @NonNull cloneToBuilder()416 public DBCustomAudience.Builder cloneToBuilder() { 417 return new DBCustomAudience.Builder() 418 .setOwner(this.mOwner) 419 .setBuyer(this.mBuyer) 420 .setName(this.mName) 421 .setExpirationTime(this.mExpirationTime) 422 .setActivationTime(this.mActivationTime) 423 .setCreationTime(this.mCreationTime) 424 .setLastAdsAndBiddingDataUpdatedTime(this.mLastAdsAndBiddingDataUpdatedTime) 425 .setUserBiddingSignals(this.mUserBiddingSignals) 426 .setTrustedBiddingData(this.mTrustedBiddingData) 427 .setBiddingLogicUri(this.mBiddingLogicUri) 428 .setAds(this.mAds) 429 .setDebuggable(this.mDebuggable) 430 .setAuctionServerRequestFlags(this.mAuctionServerRequestFlags); 431 } 432 433 @Override toString()434 public String toString() { 435 return "DBCustomAudience{" 436 + "mOwner='" 437 + mOwner 438 + '\'' 439 + ", mBuyer=" 440 + mBuyer 441 + ", mName='" 442 + mName 443 + '\'' 444 + ", mExpirationTime=" 445 + mExpirationTime 446 + ", mActivationTime=" 447 + mActivationTime 448 + ", mCreationTime=" 449 + mCreationTime 450 + ", mLastAdsAndBiddingDataUpdatedTime=" 451 + mLastAdsAndBiddingDataUpdatedTime 452 + ", mUserBiddingSignals=" 453 + mUserBiddingSignals 454 + ", mTrustedBiddingData=" 455 + mTrustedBiddingData 456 + ", mBiddingLogicUri=" 457 + mBiddingLogicUri 458 + ", mAds=" 459 + mAds 460 + ", mDebuggable=" 461 + mDebuggable 462 + ", mAuctionServerRequestFlags=" 463 + mAuctionServerRequestFlags 464 + '}'; 465 } 466 467 /** Builder to construct a {@link DBCustomAudience}. */ 468 public static final class Builder { 469 private String mOwner; 470 private AdTechIdentifier mBuyer; 471 private String mName; 472 private Instant mExpirationTime; 473 private Instant mActivationTime; 474 private Instant mCreationTime; 475 private Instant mLastAdsAndBiddingDataUpdatedTime; 476 private AdSelectionSignals mUserBiddingSignals; 477 private DBTrustedBiddingData mTrustedBiddingData; 478 private Uri mBiddingLogicUri; 479 private List<DBAdData> mAds; 480 private boolean mDebuggable; 481 @CustomAudience.AuctionServerRequestFlag private int mAuctionServerRequestFlags; 482 Builder()483 public Builder() {} 484 Builder(@onNull DBCustomAudience customAudience)485 public Builder(@NonNull DBCustomAudience customAudience) { 486 Objects.requireNonNull(customAudience, "Custom audience must not be null."); 487 488 mOwner = customAudience.getOwner(); 489 mBuyer = customAudience.getBuyer(); 490 mName = customAudience.getName(); 491 mExpirationTime = customAudience.getExpirationTime(); 492 mActivationTime = customAudience.getActivationTime(); 493 mCreationTime = customAudience.getCreationTime(); 494 mLastAdsAndBiddingDataUpdatedTime = 495 customAudience.getLastAdsAndBiddingDataUpdatedTime(); 496 mUserBiddingSignals = customAudience.getUserBiddingSignals(); 497 mTrustedBiddingData = customAudience.getTrustedBiddingData(); 498 mBiddingLogicUri = customAudience.getBiddingLogicUri(); 499 mAds = customAudience.getAds(); 500 mDebuggable = customAudience.isDebuggable(); 501 mAuctionServerRequestFlags = customAudience.getAuctionServerRequestFlags(); 502 } 503 504 /** See {@link #getOwner()} for detail. */ setOwner(String owner)505 public Builder setOwner(String owner) { 506 mOwner = owner; 507 return this; 508 } 509 510 /** See {@link #getBuyer()} for detail. */ setBuyer(AdTechIdentifier buyer)511 public Builder setBuyer(AdTechIdentifier buyer) { 512 mBuyer = buyer; 513 return this; 514 } 515 516 /** See {@link #getName()} for detail. */ setName(String name)517 public Builder setName(String name) { 518 mName = name; 519 return this; 520 } 521 522 /** See {@link #getExpirationTime()} for detail. */ setExpirationTime(Instant expirationTime)523 public Builder setExpirationTime(Instant expirationTime) { 524 mExpirationTime = expirationTime; 525 return this; 526 } 527 528 /** See {@link #getActivationTime()} for detail. */ setActivationTime(Instant activationTime)529 public Builder setActivationTime(Instant activationTime) { 530 mActivationTime = activationTime; 531 return this; 532 } 533 534 /** See {@link #getCreationTime()} for detail. */ setCreationTime(Instant creationTime)535 public Builder setCreationTime(Instant creationTime) { 536 mCreationTime = creationTime; 537 return this; 538 } 539 540 /** See {@link #getLastAdsAndBiddingDataUpdatedTime()} for detail. */ setLastAdsAndBiddingDataUpdatedTime( Instant lastAdsAndBiddingDataUpdatedTime)541 public Builder setLastAdsAndBiddingDataUpdatedTime( 542 Instant lastAdsAndBiddingDataUpdatedTime) { 543 mLastAdsAndBiddingDataUpdatedTime = lastAdsAndBiddingDataUpdatedTime; 544 return this; 545 } 546 547 /** See {@link #getUserBiddingSignals()} for detail. */ setUserBiddingSignals(AdSelectionSignals userBiddingSignals)548 public Builder setUserBiddingSignals(AdSelectionSignals userBiddingSignals) { 549 mUserBiddingSignals = userBiddingSignals; 550 return this; 551 } 552 553 /** See {@link #getTrustedBiddingData()} for detail. */ setTrustedBiddingData(DBTrustedBiddingData trustedBiddingData)554 public Builder setTrustedBiddingData(DBTrustedBiddingData trustedBiddingData) { 555 mTrustedBiddingData = trustedBiddingData; 556 return this; 557 } 558 559 /** See {@link #getBiddingLogicUri()} for detail. */ setBiddingLogicUri(Uri biddingLogicUri)560 public Builder setBiddingLogicUri(Uri biddingLogicUri) { 561 mBiddingLogicUri = biddingLogicUri; 562 return this; 563 } 564 565 /** See {@link #getAds()} for detail. */ setAds(List<DBAdData> ads)566 public Builder setAds(List<DBAdData> ads) { 567 mAds = ads; 568 return this; 569 } 570 571 /** See {@link #isDebuggable()} for detail. */ setDebuggable(boolean debuggable)572 public Builder setDebuggable(boolean debuggable) { 573 mDebuggable = debuggable; 574 return this; 575 } 576 577 /** Sets the bitfield of auction server request flags. */ 578 @NonNull setAuctionServerRequestFlags( @ustomAudience.AuctionServerRequestFlag int auctionServerRequestFlags)579 public Builder setAuctionServerRequestFlags( 580 @CustomAudience.AuctionServerRequestFlag int auctionServerRequestFlags) { 581 mAuctionServerRequestFlags = auctionServerRequestFlags; 582 return this; 583 } 584 585 /** 586 * Build the {@link DBCustomAudience}. 587 * 588 * @return the built {@link DBCustomAudience}. 589 */ build()590 public DBCustomAudience build() { 591 return new DBCustomAudience( 592 mOwner, 593 mBuyer, 594 mName, 595 mExpirationTime, 596 mActivationTime, 597 mCreationTime, 598 mLastAdsAndBiddingDataUpdatedTime, 599 mUserBiddingSignals, 600 mTrustedBiddingData, 601 mBiddingLogicUri, 602 mAds, 603 mDebuggable, 604 mAuctionServerRequestFlags); 605 } 606 } 607 608 /** 609 * Room DB type converters. 610 * 611 * <p>Register custom type converters here. 612 * 613 * <p>{@link TypeConverter} registered here only apply to data access with {@link 614 * DBCustomAudience} 615 */ 616 @ProvidedTypeConverter 617 public static class Converters { 618 619 private final AdDataConversionStrategy mAdDataConversionStrategy; 620 Converters( boolean frequencyCapFilteringEnabled, boolean appInstallFilteringEnabled, boolean adRenderIdEnabled)621 public Converters( 622 boolean frequencyCapFilteringEnabled, 623 boolean appInstallFilteringEnabled, 624 boolean adRenderIdEnabled) { 625 mAdDataConversionStrategy = 626 AdDataConversionStrategyFactory.getAdDataConversionStrategy( 627 frequencyCapFilteringEnabled, 628 appInstallFilteringEnabled, 629 adRenderIdEnabled); 630 } 631 632 /** Serialize {@link List<DBAdData>} to Json. */ 633 @TypeConverter 634 @Nullable toJson(@ullable List<DBAdData> adDataList)635 public String toJson(@Nullable List<DBAdData> adDataList) { 636 if (adDataList == null) { 637 return null; 638 } 639 int toJsonTraceCookie = Tracing.beginAsyncSection(Tracing.DB_CUSTOM_AUDIENCE_TO_JSON); 640 try { 641 JSONArray jsonArray = new JSONArray(); 642 for (DBAdData adData : adDataList) { 643 jsonArray.put(mAdDataConversionStrategy.toJson(adData)); 644 } 645 return jsonArray.toString(); 646 } catch (JSONException jsonException) { 647 throw new RuntimeException("Error serialize List<AdData>.", jsonException); 648 } finally { 649 Tracing.endAsyncSection(Tracing.DB_CUSTOM_AUDIENCE_TO_JSON, toJsonTraceCookie); 650 } 651 } 652 653 /** Deserialize {@link List<DBAdData>} from Json. */ 654 @TypeConverter 655 @Nullable fromJson(String json)656 public List<DBAdData> fromJson(String json) { 657 if (json == null) { 658 return null; 659 } 660 661 int fromJsonTraceCookie = 662 Tracing.beginAsyncSection(Tracing.DB_CUSTOM_AUDIENCE_FROM_JSON); 663 try { 664 JSONArray array = new JSONArray(json); 665 List<DBAdData> result = new ArrayList<>(); 666 for (int i = 0; i < array.length(); i++) { 667 JSONObject jsonObject = array.getJSONObject(i); 668 result.add(mAdDataConversionStrategy.fromJson(jsonObject).build()); 669 } 670 return result; 671 } catch (JSONException jsonException) { 672 throw new RuntimeException("Error deserialize List<AdData>.", jsonException); 673 } finally { 674 Tracing.endAsyncSection(Tracing.DB_CUSTOM_AUDIENCE_FROM_JSON, fromJsonTraceCookie); 675 } 676 } 677 } 678 } 679