1 /* 2 * Copyright 2023 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.app.appsearch.safeparcel; 18 19 import android.annotation.SuppressLint; 20 import android.app.appsearch.AppSearchBlobHandle; 21 import android.app.appsearch.EmbeddingVector; 22 import android.app.appsearch.annotation.CanIgnoreReturnValue; 23 import android.os.Parcel; 24 import android.os.Parcelable; 25 26 import org.jspecify.annotations.NonNull; 27 import org.jspecify.annotations.Nullable; 28 29 import java.util.Arrays; 30 import java.util.Objects; 31 32 /** 33 * A {@link SafeParcelable} to hold the value of a property in {@code GenericDocument#mProperties}. 34 * 35 * <p>This resembles PropertyProto in IcingLib. 36 * 37 * @hide 38 */ 39 @SafeParcelable.Class(creator = "PropertyParcelCreator") 40 // This won't be used to send data over binder, and we have to use Parcelable for code sync purpose. 41 @SuppressLint("BanParcelableUsage") 42 public final class PropertyParcel extends AbstractSafeParcelable implements Parcelable { 43 public static final Parcelable.@NonNull Creator<PropertyParcel> CREATOR = 44 new PropertyParcelCreator(); 45 46 @Field(id = 1, getter = "getPropertyName") 47 private final @NonNull String mPropertyName; 48 49 @Field(id = 2, getter = "getStringValues") 50 private final String @Nullable [] mStringValues; 51 52 @Field(id = 3, getter = "getLongValues") 53 private final long @Nullable [] mLongValues; 54 55 @Field(id = 4, getter = "getDoubleValues") 56 private final double @Nullable [] mDoubleValues; 57 58 @Field(id = 5, getter = "getBooleanValues") 59 private final boolean @Nullable [] mBooleanValues; 60 61 @Field(id = 6, getter = "getBytesValues") 62 private final byte @Nullable [][] mBytesValues; 63 64 @Field(id = 7, getter = "getDocumentValues") 65 private final GenericDocumentParcel @Nullable [] mDocumentValues; 66 67 @Field(id = 8, getter = "getEmbeddingValues") 68 private final EmbeddingVector @Nullable [] mEmbeddingValues; 69 70 @Field(id = 9, getter = "getBlobHandleValues") 71 private final AppSearchBlobHandle @Nullable [] mBlobHandleValues; 72 73 private @Nullable Integer mHashCode; 74 75 @Constructor PropertyParcel( @aramid = 1) @onNull String propertyName, @Param(id = 2) String @Nullable [] stringValues, @Param(id = 3) long @Nullable [] longValues, @Param(id = 4) double @Nullable [] doubleValues, @Param(id = 5) boolean @Nullable [] booleanValues, @Param(id = 6) byte @Nullable [][] bytesValues, @Param(id = 7) GenericDocumentParcel @Nullable [] documentValues, @Param(id = 8) EmbeddingVector @Nullable [] embeddingValues, @Param(id = 9) AppSearchBlobHandle @Nullable [] blobHandleValues)76 PropertyParcel( 77 @Param(id = 1) @NonNull String propertyName, 78 @Param(id = 2) String @Nullable [] stringValues, 79 @Param(id = 3) long @Nullable [] longValues, 80 @Param(id = 4) double @Nullable [] doubleValues, 81 @Param(id = 5) boolean @Nullable [] booleanValues, 82 @Param(id = 6) byte @Nullable [][] bytesValues, 83 @Param(id = 7) GenericDocumentParcel @Nullable [] documentValues, 84 @Param(id = 8) EmbeddingVector @Nullable [] embeddingValues, 85 @Param(id = 9) AppSearchBlobHandle @Nullable [] blobHandleValues) { 86 mPropertyName = Objects.requireNonNull(propertyName); 87 mStringValues = stringValues; 88 mLongValues = longValues; 89 mDoubleValues = doubleValues; 90 mBooleanValues = booleanValues; 91 mBytesValues = bytesValues; 92 mDocumentValues = documentValues; 93 mEmbeddingValues = embeddingValues; 94 mBlobHandleValues = blobHandleValues; 95 checkOnlyOneArrayCanBeSet(); 96 } 97 98 /** Returns the name of the property. */ getPropertyName()99 public @NonNull String getPropertyName() { 100 return mPropertyName; 101 } 102 103 /** Returns {@code String} values in an array. */ getStringValues()104 public String @Nullable [] getStringValues() { 105 return mStringValues; 106 } 107 108 /** Returns {@code long} values in an array. */ getLongValues()109 public long @Nullable [] getLongValues() { 110 return mLongValues; 111 } 112 113 /** Returns {@code double} values in an array. */ getDoubleValues()114 public double @Nullable [] getDoubleValues() { 115 return mDoubleValues; 116 } 117 118 /** Returns {@code boolean} values in an array. */ getBooleanValues()119 public boolean @Nullable [] getBooleanValues() { 120 return mBooleanValues; 121 } 122 123 /** Returns a two-dimension {@code byte} array. */ getBytesValues()124 public byte @Nullable [][] getBytesValues() { 125 return mBytesValues; 126 } 127 128 /** Returns {@link GenericDocumentParcel}s in an array. */ getDocumentValues()129 public GenericDocumentParcel @Nullable [] getDocumentValues() { 130 return mDocumentValues; 131 } 132 133 /** Returns {@link EmbeddingVector}s in an array. */ getEmbeddingValues()134 public EmbeddingVector @Nullable [] getEmbeddingValues() { 135 return mEmbeddingValues; 136 } 137 138 /** Returns {@link AppSearchBlobHandle}s in an array. */ getBlobHandleValues()139 public AppSearchBlobHandle @Nullable [] getBlobHandleValues() { 140 return mBlobHandleValues; 141 } 142 143 /** 144 * Returns the held values in an array for this property. 145 * 146 * <p>Different from other getter methods, this one will return an {@link Object}. 147 */ getValues()148 public @Nullable Object getValues() { 149 if (mStringValues != null) { 150 return mStringValues; 151 } 152 if (mLongValues != null) { 153 return mLongValues; 154 } 155 if (mDoubleValues != null) { 156 return mDoubleValues; 157 } 158 if (mBooleanValues != null) { 159 return mBooleanValues; 160 } 161 if (mBytesValues != null) { 162 return mBytesValues; 163 } 164 if (mDocumentValues != null) { 165 return mDocumentValues; 166 } 167 if (mEmbeddingValues != null) { 168 return mEmbeddingValues; 169 } 170 if (mBlobHandleValues != null) { 171 return mBlobHandleValues; 172 } 173 return null; 174 } 175 176 /** 177 * Checks there is one and only one array can be set for the property. 178 * 179 * @throws IllegalArgumentException if 0, or more than 1 arrays are set. 180 */ checkOnlyOneArrayCanBeSet()181 private void checkOnlyOneArrayCanBeSet() { 182 int notNullCount = 0; 183 if (mStringValues != null) { 184 ++notNullCount; 185 } 186 if (mLongValues != null) { 187 ++notNullCount; 188 } 189 if (mDoubleValues != null) { 190 ++notNullCount; 191 } 192 if (mBooleanValues != null) { 193 ++notNullCount; 194 } 195 if (mBytesValues != null) { 196 ++notNullCount; 197 } 198 if (mDocumentValues != null) { 199 ++notNullCount; 200 } 201 if (mEmbeddingValues != null) { 202 ++notNullCount; 203 } 204 if (mBlobHandleValues != null) { 205 ++notNullCount; 206 } 207 if (notNullCount == 0 || notNullCount > 1) { 208 throw new IllegalArgumentException( 209 "One and only one type array can be set in PropertyParcel"); 210 } 211 } 212 213 @Override hashCode()214 public int hashCode() { 215 if (mHashCode == null) { 216 int hashCode = 0; 217 if (mStringValues != null) { 218 hashCode = Arrays.hashCode(mStringValues); 219 } else if (mLongValues != null) { 220 hashCode = Arrays.hashCode(mLongValues); 221 } else if (mDoubleValues != null) { 222 hashCode = Arrays.hashCode(mDoubleValues); 223 } else if (mBooleanValues != null) { 224 hashCode = Arrays.hashCode(mBooleanValues); 225 } else if (mBytesValues != null) { 226 hashCode = Arrays.deepHashCode(mBytesValues); 227 } else if (mDocumentValues != null) { 228 hashCode = Arrays.hashCode(mDocumentValues); 229 } else if (mEmbeddingValues != null) { 230 hashCode = Arrays.deepHashCode(mEmbeddingValues); 231 } else if (mBlobHandleValues != null) { 232 hashCode = Arrays.deepHashCode(mBlobHandleValues); 233 } 234 mHashCode = Objects.hash(mPropertyName, hashCode); 235 } 236 return mHashCode; 237 } 238 239 @Override equals(@ullable Object other)240 public boolean equals(@Nullable Object other) { 241 if (this == other) { 242 return true; 243 } 244 if (!(other instanceof PropertyParcel)) { 245 return false; 246 } 247 PropertyParcel otherPropertyParcel = (PropertyParcel) other; 248 if (!mPropertyName.equals(otherPropertyParcel.mPropertyName)) { 249 return false; 250 } 251 return Arrays.equals(mStringValues, otherPropertyParcel.mStringValues) 252 && Arrays.equals(mLongValues, otherPropertyParcel.mLongValues) 253 && Arrays.equals(mDoubleValues, otherPropertyParcel.mDoubleValues) 254 && Arrays.equals(mBooleanValues, otherPropertyParcel.mBooleanValues) 255 && Arrays.deepEquals(mBytesValues, otherPropertyParcel.mBytesValues) 256 && Arrays.equals(mDocumentValues, otherPropertyParcel.mDocumentValues) 257 && Arrays.deepEquals(mEmbeddingValues, otherPropertyParcel.mEmbeddingValues) 258 && Arrays.deepEquals(mBlobHandleValues, otherPropertyParcel.mBlobHandleValues); 259 } 260 261 @Override writeToParcel(@onNull Parcel dest, int flags)262 public void writeToParcel(@NonNull Parcel dest, int flags) { 263 PropertyParcelCreator.writeToParcel(this, dest, flags); 264 } 265 266 /** Builder for {@link PropertyParcel}. */ 267 public static final class Builder { 268 private String mPropertyName; 269 private String[] mStringValues; 270 private long[] mLongValues; 271 private double[] mDoubleValues; 272 private boolean[] mBooleanValues; 273 private byte[][] mBytesValues; 274 private GenericDocumentParcel[] mDocumentValues; 275 private EmbeddingVector[] mEmbeddingValues; 276 private AppSearchBlobHandle[] mBlobHandleValues; 277 Builder(@onNull String propertyName)278 public Builder(@NonNull String propertyName) { 279 mPropertyName = Objects.requireNonNull(propertyName); 280 } 281 282 /** Sets String values. */ 283 @CanIgnoreReturnValue setStringValues(String @onNull [] stringValues)284 public @NonNull Builder setStringValues(String @NonNull [] stringValues) { 285 mStringValues = Objects.requireNonNull(stringValues); 286 return this; 287 } 288 289 /** Sets long values. */ 290 @CanIgnoreReturnValue setLongValues(long @NonNull [] longValues)291 public @NonNull Builder setLongValues(long @NonNull [] longValues) { 292 mLongValues = Objects.requireNonNull(longValues); 293 return this; 294 } 295 296 /** Sets double values. */ 297 @CanIgnoreReturnValue setDoubleValues(double @NonNull [] doubleValues)298 public @NonNull Builder setDoubleValues(double @NonNull [] doubleValues) { 299 mDoubleValues = Objects.requireNonNull(doubleValues); 300 return this; 301 } 302 303 /** Sets boolean values. */ 304 @CanIgnoreReturnValue setBooleanValues(boolean @NonNull [] booleanValues)305 public @NonNull Builder setBooleanValues(boolean @NonNull [] booleanValues) { 306 mBooleanValues = Objects.requireNonNull(booleanValues); 307 return this; 308 } 309 310 /** Sets a two dimension byte array. */ 311 @CanIgnoreReturnValue setBytesValues(byte @NonNull [][] bytesValues)312 public @NonNull Builder setBytesValues(byte @NonNull [][] bytesValues) { 313 mBytesValues = Objects.requireNonNull(bytesValues); 314 return this; 315 } 316 317 /** Sets document values. */ 318 @CanIgnoreReturnValue setDocumentValues( GenericDocumentParcel @onNull [] documentValues)319 public @NonNull Builder setDocumentValues( 320 GenericDocumentParcel @NonNull [] documentValues) { 321 mDocumentValues = Objects.requireNonNull(documentValues); 322 return this; 323 } 324 325 /** Sets embedding values. */ 326 @CanIgnoreReturnValue setEmbeddingValues(EmbeddingVector @onNull [] embeddingValues)327 public @NonNull Builder setEmbeddingValues(EmbeddingVector @NonNull [] embeddingValues) { 328 mEmbeddingValues = Objects.requireNonNull(embeddingValues); 329 return this; 330 } 331 332 /** Sets {@link AppSearchBlobHandle} values. */ 333 @CanIgnoreReturnValue setBlobHandleValues( AppSearchBlobHandle @onNull [] blobHandleValues)334 public @NonNull Builder setBlobHandleValues( 335 AppSearchBlobHandle @NonNull [] blobHandleValues) { 336 mBlobHandleValues = Objects.requireNonNull(blobHandleValues); 337 return this; 338 } 339 340 /** Builds a {@link PropertyParcel}. */ build()341 public @NonNull PropertyParcel build() { 342 return new PropertyParcel( 343 mPropertyName, 344 mStringValues, 345 mLongValues, 346 mDoubleValues, 347 mBooleanValues, 348 mBytesValues, 349 mDocumentValues, 350 mEmbeddingValues, 351 mBlobHandleValues); 352 } 353 } 354 } 355