1 /* 2 * Copyright (C) 2021 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.aidl; 18 19 import android.annotation.NonNull; 20 import android.annotation.Nullable; 21 import android.app.appsearch.AppSearchResult; 22 import android.app.appsearch.GetSchemaResponse; 23 import android.app.appsearch.InternalSetSchemaResponse; 24 import android.app.appsearch.ParcelableUtil; 25 import android.app.appsearch.SearchResultPage; 26 import android.app.appsearch.SearchSuggestionResult; 27 import android.app.appsearch.SetSchemaResponse.MigrationFailure; 28 import android.app.appsearch.StorageInfo; 29 import android.app.appsearch.annotation.CanIgnoreReturnValue; 30 import android.app.appsearch.functions.ExecuteAppFunctionResponse; 31 import android.app.appsearch.safeparcel.AbstractSafeParcelable; 32 import android.app.appsearch.safeparcel.GenericDocumentParcel; 33 import android.app.appsearch.safeparcel.SafeParcelable; 34 import android.os.Parcel; 35 import android.os.Parcelable; 36 37 import java.util.List; 38 import java.util.Objects; 39 40 /** 41 * Parcelable wrapper around {@link AppSearchResult}. 42 * 43 * <p>{@link AppSearchResult} can contain any value, including non-parcelable values. For the 44 * specific case of sending {@link AppSearchResult} across Binder, this class wraps an {@link 45 * AppSearchResult} that contains a parcelable type and provides parcelability of the whole 46 * structure. 47 * 48 * @param <ValueType> The type of result object for successful calls. Must be a parcelable type. 49 * @hide 50 */ 51 @SafeParcelable.Class(creator = "AppSearchResultParcelCreator", creatorIsFinal = false) 52 public final class AppSearchResultParcel<ValueType> extends AbstractSafeParcelable { 53 54 @NonNull 55 @SuppressWarnings("rawtypes") 56 public static final Parcelable.Creator<AppSearchResultParcel> CREATOR = 57 new AppSearchResultParcelCreator() { 58 @Override 59 public AppSearchResultParcel createFromParcel(Parcel in) { 60 // We pass the result we get from ParcelableUtil#readBlob to 61 // AppSearchResultParcelCreator to decode. 62 byte[] dataBlob = Objects.requireNonNull(ParcelableUtil.readBlob(in)); 63 // Create a parcel object to un-serialize the byte array we are reading from 64 // Parcel.readBlob(). Parcel.WriteBlob() could take care of whether to pass 65 // data via binder directly or Android shared memory if the data is large. 66 Parcel unmarshallParcel = Parcel.obtain(); 67 try { 68 unmarshallParcel.unmarshall(dataBlob, 0, dataBlob.length); 69 unmarshallParcel.setDataPosition(0); 70 return super.createFromParcel(unmarshallParcel); 71 } finally { 72 unmarshallParcel.recycle(); 73 } 74 } 75 }; 76 77 @NonNull 78 private static final Parcelable.Creator<AppSearchResultParcel> CREATOR_WITHOUT_BLOB = 79 new AppSearchResultParcelCreator(); 80 81 @Field(id = 1) 82 @AppSearchResult.ResultCode 83 int mResultCode; 84 85 @Field(id = 2) 86 @Nullable 87 String mErrorMessage; 88 89 @Field(id = 3) 90 @Nullable 91 InternalSetSchemaResponse mInternalSetSchemaResponse; 92 93 @Field(id = 4) 94 @Nullable 95 GetSchemaResponse mGetSchemaResponse; 96 97 @Field(id = 5) 98 @Nullable 99 List<String> mStrings; 100 101 @Field(id = 6) 102 @Nullable 103 GenericDocumentParcel mGenericDocumentParcel; 104 105 @Field(id = 7) 106 @Nullable 107 SearchResultPage mSearchResultPage; 108 109 @Field(id = 8) 110 @Nullable 111 List<MigrationFailure> mMigrationFailures; 112 113 @Field(id = 9) 114 @Nullable 115 List<SearchSuggestionResult> mSearchSuggestionResults; 116 117 @Field(id = 10) 118 @Nullable 119 StorageInfo mStorageInfo; 120 121 @Field(id = 11) 122 @Nullable 123 ExecuteAppFunctionResponse mExecuteAppFunctionResponse; 124 125 @NonNull AppSearchResult<ValueType> mResultCached; 126 127 /** 128 * Creates an AppSearchResultParcel for given value type. 129 * 130 * @param resultCode A {@link AppSearchResult} result code for {@link IAppSearchManager} API 131 * response. 132 * @param errorMessage An error message in case of a failed response. 133 * @param internalSetSchemaResponse An {@link InternalSetSchemaResponse} type response. 134 * @param getSchemaResponse An {@link GetSchemaResponse} type response. 135 * @param strings An {@link List<String>} type response. 136 * @param genericDocumentParcel An {@link GenericDocumentParcel} type response. 137 * @param searchResultPage An {@link SearchResultPage} type response. 138 * @param migrationFailures An {@link List<MigrationFailure>} type response. 139 * @param searchSuggestionResults An {@link List<SearchSuggestionResult>} type response. 140 * @param storageInfo {@link StorageInfo} type response. 141 */ 142 @Constructor AppSearchResultParcel( @aramid = 1) @ppSearchResult.ResultCode int resultCode, @Param(id = 2) @Nullable String errorMessage, @Param(id = 3) @Nullable InternalSetSchemaResponse internalSetSchemaResponse, @Param(id = 4) @Nullable GetSchemaResponse getSchemaResponse, @Param(id = 5) @Nullable List<String> strings, @Param(id = 6) @Nullable GenericDocumentParcel genericDocumentParcel, @Param(id = 7) @Nullable SearchResultPage searchResultPage, @Param(id = 8) @Nullable List<MigrationFailure> migrationFailures, @Param(id = 9) @Nullable List<SearchSuggestionResult> searchSuggestionResults, @Param(id = 10) @Nullable StorageInfo storageInfo, @Param(id = 11) @Nullable ExecuteAppFunctionResponse executeAppFunctionResponse)143 AppSearchResultParcel( 144 @Param(id = 1) @AppSearchResult.ResultCode int resultCode, 145 @Param(id = 2) @Nullable String errorMessage, 146 @Param(id = 3) @Nullable InternalSetSchemaResponse internalSetSchemaResponse, 147 @Param(id = 4) @Nullable GetSchemaResponse getSchemaResponse, 148 @Param(id = 5) @Nullable List<String> strings, 149 @Param(id = 6) @Nullable GenericDocumentParcel genericDocumentParcel, 150 @Param(id = 7) @Nullable SearchResultPage searchResultPage, 151 @Param(id = 8) @Nullable List<MigrationFailure> migrationFailures, 152 @Param(id = 9) @Nullable List<SearchSuggestionResult> searchSuggestionResults, 153 @Param(id = 10) @Nullable StorageInfo storageInfo, 154 @Param(id = 11) @Nullable ExecuteAppFunctionResponse executeAppFunctionResponse) { 155 mResultCode = resultCode; 156 mErrorMessage = errorMessage; 157 if (resultCode == AppSearchResult.RESULT_OK) { 158 mInternalSetSchemaResponse = internalSetSchemaResponse; 159 mGetSchemaResponse = getSchemaResponse; 160 mStrings = strings; 161 mGenericDocumentParcel = genericDocumentParcel; 162 mSearchResultPage = searchResultPage; 163 mMigrationFailures = migrationFailures; 164 mSearchSuggestionResults = searchSuggestionResults; 165 mStorageInfo = storageInfo; 166 mExecuteAppFunctionResponse = executeAppFunctionResponse; 167 if (mInternalSetSchemaResponse != null) { 168 mResultCached = 169 (AppSearchResult<ValueType>) 170 AppSearchResult.newSuccessfulResult(mInternalSetSchemaResponse); 171 } else if (mGetSchemaResponse != null) { 172 mResultCached = 173 (AppSearchResult<ValueType>) 174 AppSearchResult.newSuccessfulResult(mGetSchemaResponse); 175 } else if (mStrings != null) { 176 mResultCached = 177 (AppSearchResult<ValueType>) AppSearchResult.newSuccessfulResult(mStrings); 178 } else if (mGenericDocumentParcel != null) { 179 mResultCached = 180 (AppSearchResult<ValueType>) 181 AppSearchResult.newSuccessfulResult(mGenericDocumentParcel); 182 } else if (mSearchResultPage != null) { 183 mResultCached = 184 (AppSearchResult<ValueType>) 185 AppSearchResult.newSuccessfulResult(mSearchResultPage); 186 } else if (mMigrationFailures != null) { 187 mResultCached = 188 (AppSearchResult<ValueType>) 189 AppSearchResult.newSuccessfulResult(mMigrationFailures); 190 } else if (mSearchSuggestionResults != null) { 191 mResultCached = 192 (AppSearchResult<ValueType>) 193 AppSearchResult.newSuccessfulResult(mSearchSuggestionResults); 194 } else if (mStorageInfo != null) { 195 mResultCached = 196 (AppSearchResult<ValueType>) 197 AppSearchResult.newSuccessfulResult(mStorageInfo); 198 } else if (mExecuteAppFunctionResponse != null) { 199 mResultCached = 200 (AppSearchResult<ValueType>) 201 AppSearchResult.newSuccessfulResult(mExecuteAppFunctionResponse); 202 } else { 203 // Default case where code is OK and value is null. 204 mResultCached = AppSearchResult.newSuccessfulResult(null); 205 } 206 } else { 207 mResultCached = AppSearchResult.newFailedResult(mResultCode, mErrorMessage); 208 } 209 } 210 211 /** 212 * Creates a new {@link AppSearchResultParcel} from the given result in case a successful Void 213 * response. 214 */ fromVoid()215 public static AppSearchResultParcel fromVoid() { 216 return new AppSearchResultParcel.Builder<>(AppSearchResult.RESULT_OK).build(); 217 } 218 219 /** Creates a new failed {@link AppSearchResultParcel} from result code and error message. */ 220 @SuppressWarnings({"unchecked", "rawtypes"}) fromFailedResult(AppSearchResult failedResult)221 public static AppSearchResultParcel fromFailedResult(AppSearchResult failedResult) { 222 if (failedResult.isSuccess()) { 223 throw new IllegalStateException( 224 "Creating a failed AppSearchResultParcel from a " + "successful response"); 225 } 226 227 return new AppSearchResultParcel.Builder<>(failedResult.getResultCode()) 228 .setErrorMessage(failedResult.getErrorMessage()) 229 .build(); 230 } 231 232 /** 233 * Creates a new {@link AppSearchResultParcel} from the given result in case a successful {@link 234 * InternalSetSchemaResponse}. 235 */ fromInternalSetSchemaResponse( InternalSetSchemaResponse internalSetSchemaResponse)236 public static AppSearchResultParcel<InternalSetSchemaResponse> fromInternalSetSchemaResponse( 237 InternalSetSchemaResponse internalSetSchemaResponse) { 238 return new AppSearchResultParcel.Builder<InternalSetSchemaResponse>( 239 AppSearchResult.RESULT_OK) 240 .setInternalSetSchemaResponse(internalSetSchemaResponse) 241 .build(); 242 } 243 244 /** 245 * Creates a new {@link AppSearchResultParcel} from the given result in case a successful {@link 246 * GetSchemaResponse}. 247 */ fromGetSchemaResponse( GetSchemaResponse getSchemaResponse)248 public static AppSearchResultParcel<GetSchemaResponse> fromGetSchemaResponse( 249 GetSchemaResponse getSchemaResponse) { 250 return new AppSearchResultParcel.Builder<GetSchemaResponse>(AppSearchResult.RESULT_OK) 251 .setGetSchemaResponse(getSchemaResponse) 252 .build(); 253 } 254 255 /** 256 * Creates a new {@link AppSearchResultParcel} from the given result in case a successful {@link 257 * List}<{@link String}>. 258 */ fromStringList(List<String> stringList)259 public static AppSearchResultParcel<List<String>> fromStringList(List<String> stringList) { 260 return new AppSearchResultParcel.Builder<List<String>>(AppSearchResult.RESULT_OK) 261 .setStrings(stringList) 262 .build(); 263 } 264 265 /** 266 * Creates a new {@link AppSearchResultParcel} from the given result in case a successful {@link 267 * GenericDocumentParcel}. 268 */ fromGenericDocumentParcel( GenericDocumentParcel genericDocumentParcel)269 public static AppSearchResultParcel<GenericDocumentParcel> fromGenericDocumentParcel( 270 GenericDocumentParcel genericDocumentParcel) { 271 return new AppSearchResultParcel.Builder<GenericDocumentParcel>(AppSearchResult.RESULT_OK) 272 .setGenericDocumentParcel(genericDocumentParcel) 273 .build(); 274 } 275 276 /** 277 * Creates a new {@link AppSearchResultParcel} from the given result in case a successful {@link 278 * SearchResultPage}. 279 */ fromSearchResultPage( SearchResultPage searchResultPage)280 public static AppSearchResultParcel<SearchResultPage> fromSearchResultPage( 281 SearchResultPage searchResultPage) { 282 return new AppSearchResultParcel.Builder<SearchResultPage>(AppSearchResult.RESULT_OK) 283 .setSearchResultPage(searchResultPage) 284 .build(); 285 } 286 287 /** 288 * Creates a new {@link AppSearchResultParcel} from the given result in case a successful {@link 289 * List}<{@link MigrationFailure}>. 290 */ fromMigrationFailuresList( List<MigrationFailure> migrationFailureList)291 public static AppSearchResultParcel<List<MigrationFailure>> fromMigrationFailuresList( 292 List<MigrationFailure> migrationFailureList) { 293 return new AppSearchResultParcel.Builder<List<MigrationFailure>>(AppSearchResult.RESULT_OK) 294 .setMigrationFailures(migrationFailureList) 295 .build(); 296 } 297 298 /** 299 * Creates a new {@link AppSearchResultParcel} from the given result in case a successful {@link 300 * List}<{@link SearchSuggestionResult}>. 301 */ 302 public static AppSearchResultParcel<List<SearchSuggestionResult>> fromSearchSuggestionResultList( List<SearchSuggestionResult> searchSuggestionResultList)303 fromSearchSuggestionResultList( 304 List<SearchSuggestionResult> searchSuggestionResultList) { 305 return new AppSearchResultParcel.Builder<List<SearchSuggestionResult>>( 306 AppSearchResult.RESULT_OK) 307 .setSearchSuggestionResults(searchSuggestionResultList) 308 .build(); 309 } 310 311 /** 312 * Creates a new {@link AppSearchResultParcel} from the given result in case a successful {@link 313 * StorageInfo}. 314 */ fromStorageInfo(StorageInfo storageInfo)315 public static AppSearchResultParcel<StorageInfo> fromStorageInfo(StorageInfo storageInfo) { 316 return new AppSearchResultParcel.Builder<StorageInfo>(AppSearchResult.RESULT_OK) 317 .setStorageInfo(storageInfo) 318 .build(); 319 } 320 321 /** 322 * Creates a new {@link AppSearchResultParcel} from the given result in case a successful {@link 323 * ExecuteAppFunctionResponse}. 324 */ fromExecuteAppFunctionResponse( ExecuteAppFunctionResponse executeAppFunctionResponse)325 public static AppSearchResultParcel<ExecuteAppFunctionResponse> fromExecuteAppFunctionResponse( 326 ExecuteAppFunctionResponse executeAppFunctionResponse) { 327 return new AppSearchResultParcel.Builder<ExecuteAppFunctionResponse>( 328 AppSearchResult.RESULT_OK) 329 .setExecuteAppFunctionResponse(executeAppFunctionResponse) 330 .build(); 331 } 332 333 @NonNull getResult()334 public AppSearchResult<ValueType> getResult() { 335 return mResultCached; 336 } 337 338 /** @hide */ 339 @Override writeToParcel(@onNull Parcel dest, int flags)340 public void writeToParcel(@NonNull Parcel dest, int flags) { 341 // Serializes the whole object, So that we can use Parcel.writeBlob() to send data. 342 // WriteBlob() could take care of whether to pass data via binder directly or Android shared 343 // memory if the data is large. 344 byte[] bytes; 345 Parcel data = Parcel.obtain(); 346 try { 347 // We pass encoded result from AppSearchResultParcelCreator to ParcelableUtil#writeBlob. 348 directlyWriteToParcel(this, data, flags); 349 bytes = data.marshall(); 350 } finally { 351 data.recycle(); 352 } 353 ParcelableUtil.writeBlob(dest, bytes); 354 } 355 directlyWriteToParcel( @onNull AppSearchResultParcel<?> result, @NonNull Parcel data, int flags)356 static void directlyWriteToParcel( 357 @NonNull AppSearchResultParcel<?> result, @NonNull Parcel data, int flags) { 358 AppSearchResultParcelCreator.writeToParcel(result, data, flags); 359 } 360 directlyReadFromParcel(@onNull Parcel data)361 static AppSearchResultParcel<?> directlyReadFromParcel(@NonNull Parcel data) { 362 return CREATOR_WITHOUT_BLOB.createFromParcel(data); 363 } 364 365 /** 366 * Builder for {@link AppSearchResultParcel} objects. 367 * 368 * @param <ValueType> The type of the result objects for successful results. 369 */ 370 static final class Builder<ValueType> { 371 372 @AppSearchResult.ResultCode private final int mResultCode; 373 @Nullable private String mErrorMessage; 374 @Nullable private InternalSetSchemaResponse mInternalSetSchemaResponse; 375 @Nullable private GetSchemaResponse mGetSchemaResponse; 376 @Nullable private List<String> mStrings; 377 @Nullable private GenericDocumentParcel mGenericDocumentParcel; 378 @Nullable private SearchResultPage mSearchResultPage; 379 @Nullable private List<MigrationFailure> mMigrationFailures; 380 @Nullable private List<SearchSuggestionResult> mSearchSuggestionResults; 381 @Nullable private StorageInfo mStorageInfo; 382 @Nullable private ExecuteAppFunctionResponse mExecuteAppFunctionResponse; 383 384 /** Builds an {@link AppSearchResultParcel.Builder}. */ Builder(int resultCode)385 Builder(int resultCode) { 386 mResultCode = resultCode; 387 } 388 389 @CanIgnoreReturnValue setErrorMessage(@ullable String errorMessage)390 Builder<ValueType> setErrorMessage(@Nullable String errorMessage) { 391 mErrorMessage = errorMessage; 392 return this; 393 } 394 395 @CanIgnoreReturnValue setInternalSetSchemaResponse( InternalSetSchemaResponse internalSetSchemaResponse)396 Builder<ValueType> setInternalSetSchemaResponse( 397 InternalSetSchemaResponse internalSetSchemaResponse) { 398 mInternalSetSchemaResponse = internalSetSchemaResponse; 399 return this; 400 } 401 402 @CanIgnoreReturnValue setGetSchemaResponse(GetSchemaResponse getSchemaResponse)403 Builder<ValueType> setGetSchemaResponse(GetSchemaResponse getSchemaResponse) { 404 mGetSchemaResponse = getSchemaResponse; 405 return this; 406 } 407 408 @CanIgnoreReturnValue setStrings(List<String> strings)409 Builder<ValueType> setStrings(List<String> strings) { 410 mStrings = strings; 411 return this; 412 } 413 414 @CanIgnoreReturnValue setGenericDocumentParcel(GenericDocumentParcel genericDocumentParcel)415 Builder<ValueType> setGenericDocumentParcel(GenericDocumentParcel genericDocumentParcel) { 416 mGenericDocumentParcel = genericDocumentParcel; 417 return this; 418 } 419 420 @CanIgnoreReturnValue setSearchResultPage(SearchResultPage searchResultPage)421 Builder<ValueType> setSearchResultPage(SearchResultPage searchResultPage) { 422 mSearchResultPage = searchResultPage; 423 return this; 424 } 425 426 @CanIgnoreReturnValue setMigrationFailures(List<MigrationFailure> migrationFailures)427 Builder<ValueType> setMigrationFailures(List<MigrationFailure> migrationFailures) { 428 mMigrationFailures = migrationFailures; 429 return this; 430 } 431 432 @CanIgnoreReturnValue setSearchSuggestionResults( List<SearchSuggestionResult> searchSuggestionResults)433 Builder<ValueType> setSearchSuggestionResults( 434 List<SearchSuggestionResult> searchSuggestionResults) { 435 mSearchSuggestionResults = searchSuggestionResults; 436 return this; 437 } 438 439 @CanIgnoreReturnValue setStorageInfo(StorageInfo storageInfo)440 Builder<ValueType> setStorageInfo(StorageInfo storageInfo) { 441 mStorageInfo = storageInfo; 442 return this; 443 } 444 445 @CanIgnoreReturnValue setExecuteAppFunctionResponse( ExecuteAppFunctionResponse executeAppFunctionResponse)446 Builder<ValueType> setExecuteAppFunctionResponse( 447 ExecuteAppFunctionResponse executeAppFunctionResponse) { 448 mExecuteAppFunctionResponse = executeAppFunctionResponse; 449 return this; 450 } 451 452 /** 453 * Builds an {@link AppSearchResultParcel} object from the contents of this {@link 454 * AppSearchResultParcel.Builder}. 455 */ 456 @NonNull build()457 AppSearchResultParcel<ValueType> build() { 458 return new AppSearchResultParcel<>( 459 mResultCode, 460 mErrorMessage, 461 mInternalSetSchemaResponse, 462 mGetSchemaResponse, 463 mStrings, 464 mGenericDocumentParcel, 465 mSearchResultPage, 466 mMigrationFailures, 467 mSearchSuggestionResults, 468 mStorageInfo, 469 mExecuteAppFunctionResponse); 470 } 471 } 472 } 473