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.AppSearchBatchResult; 22 import android.app.appsearch.AppSearchResult; 23 import android.app.appsearch.ParcelableUtil; 24 import android.app.appsearch.safeparcel.AbstractSafeParcelable; 25 import android.app.appsearch.safeparcel.GenericDocumentParcel; 26 import android.app.appsearch.safeparcel.SafeParcelable; 27 import android.os.Bundle; 28 import android.os.Parcel; 29 import android.os.Parcelable; 30 31 import java.util.Map; 32 import java.util.Objects; 33 34 /** 35 * Parcelable wrapper around {@link AppSearchBatchResult}. 36 * 37 * <p>{@link AppSearchBatchResult} can contain any type of key and value, including non-parcelable 38 * values. For the specific case of sending {@link AppSearchBatchResult} across Binder, this class 39 * wraps an {@link AppSearchBatchResult} that has String keys and Parcelable values. It provides 40 * parcelability of the whole structure. 41 * 42 * @deprecated This class is deprecated, you should use {@link AppSearchBatchResultParcelV2}. 43 * @param <ValueType> The type of result object for successful calls. Must be a parcelable type. 44 * @hide 45 */ 46 @Deprecated 47 @SafeParcelable.Class(creator = "AppSearchBatchResultParcelCreator", creatorIsFinal = false) 48 public final class AppSearchBatchResultParcel<ValueType> extends AbstractSafeParcelable { 49 50 @NonNull 51 @SuppressWarnings("rawtypes") 52 public static final Parcelable.Creator<AppSearchBatchResultParcel> CREATOR = 53 new AppSearchBatchResultParcelCreator() { 54 @Override 55 public AppSearchBatchResultParcel createFromParcel(Parcel in) { 56 byte[] dataBlob = Objects.requireNonNull(ParcelableUtil.readBlob(in)); 57 Parcel unmarshallParcel = Parcel.obtain(); 58 try { 59 unmarshallParcel.unmarshall(dataBlob, 0, dataBlob.length); 60 unmarshallParcel.setDataPosition(0); 61 int size = unmarshallParcel.dataSize(); 62 Bundle inputBundle = new Bundle(); 63 while (unmarshallParcel.dataPosition() < size) { 64 String key = Objects.requireNonNull(unmarshallParcel.readString()); 65 AppSearchResultParcel appSearchResultParcel = 66 AppSearchResultParcel.directlyReadFromParcel(unmarshallParcel); 67 inputBundle.putParcelable(key, appSearchResultParcel); 68 } 69 return new AppSearchBatchResultParcel(inputBundle); 70 } finally { 71 unmarshallParcel.recycle(); 72 } 73 } 74 }; 75 76 // Map between String Key and AppSearchResultParcel Value. 77 @Field(id = 1) 78 @NonNull 79 final Bundle mAppSearchResultBundle; 80 81 @Nullable private AppSearchBatchResult<String, ValueType> mResultCached; 82 83 @Constructor AppSearchBatchResultParcel(@aramid = 1) Bundle appSearchResultBundle)84 AppSearchBatchResultParcel(@Param(id = 1) Bundle appSearchResultBundle) { 85 mAppSearchResultBundle = appSearchResultBundle; 86 } 87 88 /** 89 * Creates a new {@link AppSearchBatchResultParcel} from the given {@link GenericDocumentParcel} 90 * results. 91 */ 92 @SuppressWarnings("unchecked") 93 public static AppSearchBatchResultParcel<GenericDocumentParcel> fromStringToGenericDocumentParcel( @onNull AppSearchBatchResult<String, GenericDocumentParcel> result)94 fromStringToGenericDocumentParcel( 95 @NonNull AppSearchBatchResult<String, GenericDocumentParcel> result) { 96 Bundle appSearchResultBundle = new Bundle(); 97 for (Map.Entry<String, AppSearchResult<GenericDocumentParcel>> entry : 98 result.getAll().entrySet()) { 99 AppSearchResultParcel<GenericDocumentParcel> appSearchResultParcel; 100 // Create result from value in success case and errorMessage in 101 // failure case. 102 if (entry.getValue().isSuccess()) { 103 GenericDocumentParcel genericDocumentParcel = 104 Objects.requireNonNull(entry.getValue().getResultValue()); 105 appSearchResultParcel = 106 AppSearchResultParcel.fromGenericDocumentParcel(genericDocumentParcel); 107 } else { 108 appSearchResultParcel = AppSearchResultParcel.fromFailedResult(entry.getValue()); 109 } 110 appSearchResultBundle.putParcelable(entry.getKey(), appSearchResultParcel); 111 } 112 return new AppSearchBatchResultParcel<>(appSearchResultBundle); 113 } 114 115 /** Creates a new {@link AppSearchBatchResultParcel} from the given {@link Void} results. */ 116 @SuppressWarnings("unchecked") fromStringToVoid( @onNull AppSearchBatchResult<String, Void> result)117 public static AppSearchBatchResultParcel<Void> fromStringToVoid( 118 @NonNull AppSearchBatchResult<String, Void> result) { 119 Bundle appSearchResultBundle = new Bundle(); 120 for (Map.Entry<String, AppSearchResult<Void>> entry : result.getAll().entrySet()) { 121 AppSearchResultParcel<Void> appSearchResultParcel; 122 // Create result from value in success case and errorMessage in 123 // failure case. 124 if (entry.getValue().isSuccess()) { 125 appSearchResultParcel = AppSearchResultParcel.fromVoid(); 126 } else { 127 appSearchResultParcel = AppSearchResultParcel.fromFailedResult(entry.getValue()); 128 } 129 appSearchResultBundle.putParcelable(entry.getKey(), appSearchResultParcel); 130 } 131 return new AppSearchBatchResultParcel<>(appSearchResultBundle); 132 } 133 134 @NonNull 135 @SuppressWarnings("unchecked") getResult()136 public AppSearchBatchResult<String, ValueType> getResult() { 137 if (mResultCached == null) { 138 AppSearchBatchResult.Builder<String, ValueType> builder = 139 new AppSearchBatchResult.Builder<>(); 140 for (String key : mAppSearchResultBundle.keySet()) { 141 builder.setResult( 142 key, 143 mAppSearchResultBundle 144 .getParcelable(key, AppSearchResultParcel.class) 145 .getResult()); 146 } 147 mResultCached = builder.build(); 148 } 149 return mResultCached; 150 } 151 152 /** @hide */ 153 @Override 154 @SuppressWarnings("unchecked") writeToParcel(@onNull Parcel dest, int flags)155 public void writeToParcel(@NonNull Parcel dest, int flags) { 156 byte[] bytes; 157 // Create a parcel object to serialize results. So that we can use Parcel.writeBlob() to 158 // send data. WriteBlob() could take care of whether to pass data via binder directly or 159 // Android shared memory if the data is large. 160 Parcel data = Parcel.obtain(); 161 try { 162 for (String key : mAppSearchResultBundle.keySet()) { 163 data.writeString(key); 164 AppSearchResultParcel<ValueType> appSearchResultParcel = 165 mAppSearchResultBundle.getParcelable(key, AppSearchResultParcel.class); 166 AppSearchResultParcel.directlyWriteToParcel(appSearchResultParcel, data, flags); 167 } 168 bytes = data.marshall(); 169 } finally { 170 data.recycle(); 171 } 172 ParcelableUtil.writeBlob(dest, bytes); 173 } 174 } 175