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 android.adservices.topics; 18 19 import static android.adservices.common.AdServicesStatusUtils.STATUS_SUCCESS; 20 21 import android.adservices.common.AdServicesResponse; 22 import android.adservices.common.AdServicesStatusUtils; 23 import android.annotation.NonNull; 24 import android.annotation.Nullable; 25 import android.os.Parcel; 26 import android.os.Parcelable; 27 28 import java.util.ArrayList; 29 import java.util.Arrays; 30 import java.util.Collections; 31 import java.util.List; 32 import java.util.Objects; 33 34 /** 35 * Represent the result from the getTopics API. 36 * 37 * @hide 38 */ 39 public final class GetTopicsResult extends AdServicesResponse { 40 private final List<Long> mTaxonomyVersions; 41 private final List<Long> mModelVersions; 42 private final List<Integer> mTopics; 43 private final List<byte[]> mEncryptedTopics; 44 private final List<String> mEncryptionKeys; 45 private final List<byte[]> mEncapsulatedKeys; 46 GetTopicsResult( @dServicesStatusUtils.StatusCode int resultCode, String errorMessage, List<Long> taxonomyVersions, List<Long> modelVersions, List<Integer> topics, List<byte[]> encryptedTopics, List<String> encryptionKeys, List<byte[]> encapsulatedKeys)47 private GetTopicsResult( 48 @AdServicesStatusUtils.StatusCode int resultCode, 49 String errorMessage, 50 List<Long> taxonomyVersions, 51 List<Long> modelVersions, 52 List<Integer> topics, 53 List<byte[]> encryptedTopics, 54 List<String> encryptionKeys, 55 List<byte[]> encapsulatedKeys) { 56 super(resultCode, errorMessage); 57 mTaxonomyVersions = taxonomyVersions; 58 mModelVersions = modelVersions; 59 mTopics = topics; 60 mEncryptedTopics = encryptedTopics; 61 mEncryptionKeys = encryptionKeys; 62 mEncapsulatedKeys = encapsulatedKeys; 63 } 64 GetTopicsResult(@onNull Parcel in)65 private GetTopicsResult(@NonNull Parcel in) { 66 super(in.readInt(), in.readString()); 67 68 mTaxonomyVersions = Collections.unmodifiableList(readLongList(in)); 69 mModelVersions = Collections.unmodifiableList(readLongList(in)); 70 mTopics = Collections.unmodifiableList(readIntegerList(in)); 71 mEncryptedTopics = Collections.unmodifiableList(readByteArrayList(in)); 72 mEncryptionKeys = Collections.unmodifiableList(readStringList(in)); 73 mEncapsulatedKeys = Collections.unmodifiableList(readByteArrayList(in)); 74 } 75 76 public static final @NonNull Creator<GetTopicsResult> CREATOR = 77 new Parcelable.Creator<GetTopicsResult>() { 78 @Override 79 public GetTopicsResult createFromParcel(Parcel in) { 80 return new GetTopicsResult(in); 81 } 82 83 @Override 84 public GetTopicsResult[] newArray(int size) { 85 return new GetTopicsResult[size]; 86 } 87 }; 88 89 /** @hide */ 90 @Override describeContents()91 public int describeContents() { 92 return 0; 93 } 94 95 /** @hide */ 96 @Override writeToParcel(@onNull Parcel out, int flags)97 public void writeToParcel(@NonNull Parcel out, int flags) { 98 out.writeInt(mStatusCode); 99 out.writeString(mErrorMessage); 100 writeLongList(out, mTaxonomyVersions); 101 writeLongList(out, mModelVersions); 102 writeIntegerList(out, mTopics); 103 writeByteArrayList(out, mEncryptedTopics); 104 writeStringList(out, mEncryptionKeys); 105 writeByteArrayList(out, mEncapsulatedKeys); 106 } 107 108 /** 109 * Returns {@code true} if {@link #getResultCode} equals {@link 110 * AdServicesStatusUtils#STATUS_SUCCESS}. 111 */ isSuccess()112 public boolean isSuccess() { 113 return getResultCode() == STATUS_SUCCESS; 114 } 115 116 /** Returns one of the {@code RESULT} constants defined in {@link GetTopicsResult}. */ getResultCode()117 public @AdServicesStatusUtils.StatusCode int getResultCode() { 118 return mStatusCode; 119 } 120 121 /** 122 * Returns the error message associated with this result. 123 * 124 * <p>If {@link #isSuccess} is {@code true}, the error message is always {@code null}. The error 125 * message may be {@code null} even if {@link #isSuccess} is {@code false}. 126 */ 127 @Nullable getErrorMessage()128 public String getErrorMessage() { 129 return mErrorMessage; 130 } 131 132 /** Get the Taxonomy Versions. */ getTaxonomyVersions()133 public List<Long> getTaxonomyVersions() { 134 return mTaxonomyVersions; 135 } 136 137 /** Get the Model Versions. */ getModelVersions()138 public List<Long> getModelVersions() { 139 return mModelVersions; 140 } 141 142 @NonNull getTopics()143 public List<Integer> getTopics() { 144 return mTopics; 145 } 146 147 @NonNull getEncryptedTopics()148 public List<byte[]> getEncryptedTopics() { 149 return mEncryptedTopics; 150 } 151 152 @NonNull getEncryptionKeys()153 public List<String> getEncryptionKeys() { 154 return mEncryptionKeys; 155 } 156 157 @NonNull getEncapsulatedKeys()158 public List<byte[]> getEncapsulatedKeys() { 159 return mEncapsulatedKeys; 160 } 161 162 @Override toString()163 public String toString() { 164 return "GetTopicsResult{" 165 + "mResultCode=" 166 + mStatusCode 167 + ", mErrorMessage='" 168 + mErrorMessage 169 + '\'' 170 + ", mTaxonomyVersions=" 171 + mTaxonomyVersions 172 + ", mModelVersions=" 173 + mModelVersions 174 + ", mTopics=" 175 + mTopics 176 + ", mEncryptedTopics=" 177 + prettyPrint(mEncryptedTopics) 178 + ", mEncryptionKeys=" 179 + mEncryptionKeys 180 + ", mEncapsulatedKeys=" 181 + prettyPrint(mEncapsulatedKeys) 182 + '}'; 183 } 184 prettyPrint(List<byte[]> listOfByteArrays)185 private String prettyPrint(List<byte[]> listOfByteArrays) { 186 StringBuilder stringBuilder = new StringBuilder(); 187 stringBuilder.append("["); 188 for (int index = 0; index < listOfByteArrays.size(); index++) { 189 stringBuilder.append(Arrays.toString(listOfByteArrays.get(index))); 190 if (index != listOfByteArrays.size() - 1) { 191 stringBuilder.append(", "); 192 } 193 } 194 stringBuilder.append("]"); 195 return stringBuilder.toString(); 196 } 197 198 @Override equals(Object o)199 public boolean equals(Object o) { 200 if (this == o) { 201 return true; 202 } 203 204 if (!(o instanceof GetTopicsResult)) { 205 return false; 206 } 207 208 GetTopicsResult that = (GetTopicsResult) o; 209 210 return mStatusCode == that.mStatusCode 211 && Objects.equals(mErrorMessage, that.mErrorMessage) 212 && mTaxonomyVersions.equals(that.mTaxonomyVersions) 213 && mModelVersions.equals(that.mModelVersions) 214 && mTopics.equals(that.mTopics) 215 && equals(mEncryptedTopics, that.mEncryptedTopics) 216 && mEncryptionKeys.equals(that.mEncryptionKeys); 217 } 218 equals(List<byte[]> list1, List<byte[]> list2)219 private static boolean equals(List<byte[]> list1, List<byte[]> list2) { 220 if (list1 == null || list2 == null) { 221 return false; 222 } 223 if (list1.size() != list2.size()) { 224 return false; 225 } 226 for (int i = 0; i < list1.size(); i++) { 227 if (!Arrays.equals(list1.get(i), list2.get(i))) { 228 return false; 229 } 230 } 231 return true; 232 } 233 234 @Override hashCode()235 public int hashCode() { 236 return Objects.hash( 237 mStatusCode, 238 mErrorMessage, 239 mTaxonomyVersions, 240 mModelVersions, 241 mTopics, 242 hashCode(mEncryptedTopics), 243 mEncryptionKeys, 244 hashCode(mEncryptedTopics)); 245 } 246 hashCode(List<byte[]> list)247 private static int hashCode(List<byte[]> list) { 248 int hash = 0; 249 for (byte[] bytes : list) { 250 hash += Arrays.hashCode(bytes); 251 } 252 return hash; 253 } 254 255 // Read the list of long from parcel. readLongList(@onNull Parcel in)256 private static List<Long> readLongList(@NonNull Parcel in) { 257 List<Long> list = new ArrayList<>(); 258 259 int toReadCount = in.readInt(); 260 // Negative toReadCount is handled implicitly 261 for (int i = 0; i < toReadCount; i++) { 262 list.add(in.readLong()); 263 } 264 265 return list; 266 } 267 268 // Read the list of integer from parcel. readIntegerList(@onNull Parcel in)269 private static List<Integer> readIntegerList(@NonNull Parcel in) { 270 List<Integer> list = new ArrayList<>(); 271 272 int toReadCount = in.readInt(); 273 // Negative toReadCount is handled implicitly 274 for (int i = 0; i < toReadCount; i++) { 275 list.add(in.readInt()); 276 } 277 278 return list; 279 } 280 281 // Read the list of integer from parcel. readStringList(@onNull Parcel in)282 private static List<String> readStringList(@NonNull Parcel in) { 283 List<String> list = new ArrayList<>(); 284 285 int toReadCount = in.readInt(); 286 // Negative toReadCount is handled implicitly 287 for (int i = 0; i < toReadCount; i++) { 288 list.add(in.readString()); 289 } 290 291 return list; 292 } 293 294 // Read the list of byte arrays from parcel. readByteArrayList(@onNull Parcel in)295 private static List<byte[]> readByteArrayList(@NonNull Parcel in) { 296 List<byte[]> list = new ArrayList<>(); 297 298 int toReadCount = in.readInt(); 299 // Negative toReadCount is handled implicitly 300 for (int i = 0; i < toReadCount; i++) { 301 list.add(in.createByteArray()); 302 } 303 304 return list; 305 } 306 307 // Write a List of Long to parcel. writeLongList(@onNull Parcel out, @Nullable List<Long> val)308 private static void writeLongList(@NonNull Parcel out, @Nullable List<Long> val) { 309 if (val == null) { 310 out.writeInt(-1); 311 return; 312 } 313 out.writeInt(val.size()); 314 for (Long l : val) { 315 out.writeLong(l); 316 } 317 } 318 319 // Write a List of Integer to parcel. writeIntegerList(@onNull Parcel out, @Nullable List<Integer> val)320 private static void writeIntegerList(@NonNull Parcel out, @Nullable List<Integer> val) { 321 if (val == null) { 322 out.writeInt(-1); 323 return; 324 } 325 out.writeInt(val.size()); 326 for (Integer integer : val) { 327 out.writeInt(integer); 328 } 329 } 330 331 // Write a List of String to parcel. writeStringList(@onNull Parcel out, @Nullable List<String> val)332 private static void writeStringList(@NonNull Parcel out, @Nullable List<String> val) { 333 if (val == null) { 334 out.writeInt(-1); 335 return; 336 } 337 out.writeInt(val.size()); 338 for (String string : val) { 339 out.writeString(string); 340 } 341 } 342 343 // Write a List of byte array to parcel. writeByteArrayList(@onNull Parcel out, @Nullable List<byte[]> val)344 private static void writeByteArrayList(@NonNull Parcel out, @Nullable List<byte[]> val) { 345 if (val == null) { 346 out.writeInt(-1); 347 return; 348 } 349 out.writeInt(val.size()); 350 for (byte[] bytes : val) { 351 out.writeByteArray(bytes); 352 } 353 } 354 355 /** 356 * Builder for {@link GetTopicsResult} objects. 357 * 358 * @hide 359 */ 360 public static final class Builder { 361 private @AdServicesStatusUtils.StatusCode int mResultCode; 362 private String mErrorMessage; 363 private List<Long> mTaxonomyVersions = new ArrayList<>(); 364 private List<Long> mModelVersions = new ArrayList<>(); 365 private List<Integer> mTopics = new ArrayList<>(); 366 private List<byte[]> mEncryptedTopics = new ArrayList<>(); 367 private List<String> mEncryptionKeys = new ArrayList<>(); 368 private List<byte[]> mEncapsulatedKeys = new ArrayList<>(); 369 Builder()370 public Builder() {} 371 372 /** Set the Result Code. */ 373 @NonNull setResultCode(@dServicesStatusUtils.StatusCode int resultCode)374 public Builder setResultCode(@AdServicesStatusUtils.StatusCode int resultCode) { 375 mResultCode = resultCode; 376 return this; 377 } 378 379 /** Set the Error Message. */ 380 @NonNull setErrorMessage(@ullable String errorMessage)381 public Builder setErrorMessage(@Nullable String errorMessage) { 382 mErrorMessage = errorMessage; 383 return this; 384 } 385 386 /** Set the Taxonomy Version. */ 387 @NonNull setTaxonomyVersions(@onNull List<Long> taxonomyVersions)388 public Builder setTaxonomyVersions(@NonNull List<Long> taxonomyVersions) { 389 mTaxonomyVersions = taxonomyVersions; 390 return this; 391 } 392 393 /** Set the Model Version. */ 394 @NonNull setModelVersions(@onNull List<Long> modelVersions)395 public Builder setModelVersions(@NonNull List<Long> modelVersions) { 396 mModelVersions = modelVersions; 397 return this; 398 } 399 400 /** Set the list of the returned Topics */ 401 @NonNull setTopics(@onNull List<Integer> topics)402 public Builder setTopics(@NonNull List<Integer> topics) { 403 mTopics = topics; 404 return this; 405 } 406 407 /** Set the list of the returned encrypted topics */ 408 @NonNull setEncryptedTopics(@onNull List<byte[]> encryptedTopics)409 public Builder setEncryptedTopics(@NonNull List<byte[]> encryptedTopics) { 410 mEncryptedTopics = encryptedTopics; 411 return this; 412 } 413 414 /** Set the list of the encryption keys */ 415 @NonNull setEncryptionKeys(@onNull List<String> encryptionKeys)416 public Builder setEncryptionKeys(@NonNull List<String> encryptionKeys) { 417 mEncryptionKeys = encryptionKeys; 418 return this; 419 } 420 421 /** Set the list of encapsulated keys generated via encryption */ 422 @NonNull setEncapsulatedKeys(@onNull List<byte[]> encapsulatedKeys)423 public Builder setEncapsulatedKeys(@NonNull List<byte[]> encapsulatedKeys) { 424 mEncapsulatedKeys = encapsulatedKeys; 425 return this; 426 } 427 428 /** 429 * Builds a {@link GetTopicsResult} instance. 430 * 431 * <p>throws IllegalArgumentException if any of the params are null or there is any mismatch 432 * in the size of lists. 433 */ 434 @NonNull build()435 public GetTopicsResult build() { 436 if (mTopics == null 437 || mTaxonomyVersions == null 438 || mModelVersions == null 439 || mEncryptedTopics == null 440 || mEncryptionKeys == null) { 441 throw new IllegalArgumentException( 442 "One of the mandatory params of GetTopicsResult is null"); 443 } 444 445 if (mTopics.size() != mTaxonomyVersions.size() 446 || mTopics.size() != mModelVersions.size()) { 447 throw new IllegalArgumentException("Size mismatch in Topics"); 448 } 449 450 if (mEncryptedTopics.size() != mEncryptionKeys.size() 451 || mEncryptedTopics.size() != mEncapsulatedKeys.size()) { 452 throw new IllegalArgumentException("Size mismatch in EncryptedTopic lists"); 453 } 454 455 return new GetTopicsResult( 456 mResultCode, 457 mErrorMessage, 458 mTaxonomyVersions, 459 mModelVersions, 460 mTopics, 461 mEncryptedTopics, 462 mEncryptionKeys, 463 mEncapsulatedKeys); 464 } 465 } 466 } 467