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