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}&lt;{@link String}&gt;.
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}&lt;{@link MigrationFailure}&gt;.
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}&lt;{@link SearchSuggestionResult}&gt;.
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