1 /* 2 * Copyright (C) 2022 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 com.android.server.appsearch.contactsindexer; 18 19 import static com.google.common.truth.Truth.assertThat; 20 21 import android.annotation.NonNull; 22 import android.app.appsearch.AppSearchBatchResult; 23 import android.app.appsearch.AppSearchSchema; 24 import android.app.appsearch.AppSearchSession; 25 import android.app.appsearch.BatchResultCallback; 26 import android.app.appsearch.GenericDocument; 27 import android.app.appsearch.GetByDocumentIdRequest; 28 import android.app.appsearch.SearchResult; 29 import android.app.appsearch.SearchResults; 30 import android.app.appsearch.SearchSpec; 31 import android.app.appsearch.exceptions.AppSearchException; 32 import android.util.ArraySet; 33 34 import com.android.server.appsearch.contactsindexer.appsearchtypes.ContactPoint; 35 import com.android.server.appsearch.contactsindexer.appsearchtypes.Person; 36 37 import java.util.Collection; 38 import java.util.List; 39 import java.util.Objects; 40 import java.util.Set; 41 import java.util.concurrent.CompletableFuture; 42 import java.util.concurrent.ExecutionException; 43 import java.util.concurrent.Executor; 44 45 class TestUtils { 46 // Schema 47 static final AppSearchSchema CONTACT_POINT_SCHEMA_WITH_APP_IDS_OPTIONAL = 48 new AppSearchSchema.Builder( 49 ContactPoint.SCHEMA_TYPE) 50 .addProperty(new AppSearchSchema.StringPropertyConfig.Builder( 51 ContactPoint.CONTACT_POINT_PROPERTY_LABEL) 52 .setCardinality(AppSearchSchema.PropertyConfig.CARDINALITY_OPTIONAL) 53 .setIndexingType( 54 AppSearchSchema.StringPropertyConfig.INDEXING_TYPE_PREFIXES) 55 .setTokenizerType( 56 AppSearchSchema.StringPropertyConfig.TOKENIZER_TYPE_PLAIN) 57 .build()) 58 // This is repeated in the official builtin:ContactPoint. 59 // appIds 60 .addProperty(new AppSearchSchema.StringPropertyConfig.Builder( 61 ContactPoint.CONTACT_POINT_PROPERTY_APP_ID) 62 .setCardinality(AppSearchSchema.PropertyConfig.CARDINALITY_OPTIONAL) 63 .build()) 64 // address 65 .addProperty(new AppSearchSchema.StringPropertyConfig.Builder( 66 ContactPoint.CONTACT_POINT_PROPERTY_ADDRESS) 67 .setCardinality(AppSearchSchema.PropertyConfig.CARDINALITY_REPEATED) 68 .setIndexingType( 69 AppSearchSchema.StringPropertyConfig.INDEXING_TYPE_PREFIXES) 70 .setTokenizerType( 71 AppSearchSchema.StringPropertyConfig.TOKENIZER_TYPE_PLAIN) 72 .build()) 73 // email 74 .addProperty(new AppSearchSchema.StringPropertyConfig.Builder( 75 ContactPoint.CONTACT_POINT_PROPERTY_EMAIL) 76 .setCardinality(AppSearchSchema.PropertyConfig.CARDINALITY_REPEATED) 77 .setIndexingType( 78 AppSearchSchema.StringPropertyConfig.INDEXING_TYPE_PREFIXES) 79 .setTokenizerType( 80 AppSearchSchema.StringPropertyConfig.TOKENIZER_TYPE_PLAIN) 81 .build()) 82 // telephone 83 .addProperty(new AppSearchSchema.StringPropertyConfig.Builder( 84 ContactPoint.CONTACT_POINT_PROPERTY_TELEPHONE) 85 .setCardinality(AppSearchSchema.PropertyConfig.CARDINALITY_REPEATED) 86 .setIndexingType( 87 AppSearchSchema.StringPropertyConfig.INDEXING_TYPE_PREFIXES) 88 .setTokenizerType( 89 AppSearchSchema.StringPropertyConfig.TOKENIZER_TYPE_PLAIN) 90 .build()) 91 .build(); 92 93 static final AppSearchSchema CONTACT_POINT_SCHEMA_WITH_LABEL_REPEATED = 94 new AppSearchSchema.Builder( 95 ContactPoint.SCHEMA_TYPE) 96 .addProperty(new AppSearchSchema.StringPropertyConfig.Builder( 97 ContactPoint.CONTACT_POINT_PROPERTY_LABEL) 98 .setCardinality(AppSearchSchema.PropertyConfig.CARDINALITY_REPEATED) 99 .setIndexingType( 100 AppSearchSchema.StringPropertyConfig.INDEXING_TYPE_PREFIXES) 101 .setTokenizerType( 102 AppSearchSchema.StringPropertyConfig.TOKENIZER_TYPE_PLAIN) 103 .build()) 104 // appIds 105 .addProperty(new AppSearchSchema.StringPropertyConfig.Builder( 106 ContactPoint.CONTACT_POINT_PROPERTY_APP_ID) 107 .setCardinality(AppSearchSchema.PropertyConfig.CARDINALITY_REPEATED) 108 .build()) 109 // address 110 .addProperty(new AppSearchSchema.StringPropertyConfig.Builder( 111 ContactPoint.CONTACT_POINT_PROPERTY_ADDRESS) 112 .setCardinality(AppSearchSchema.PropertyConfig.CARDINALITY_REPEATED) 113 .setIndexingType( 114 AppSearchSchema.StringPropertyConfig.INDEXING_TYPE_PREFIXES) 115 .setTokenizerType( 116 AppSearchSchema.StringPropertyConfig.TOKENIZER_TYPE_PLAIN) 117 .build()) 118 // email 119 .addProperty(new AppSearchSchema.StringPropertyConfig.Builder( 120 ContactPoint.CONTACT_POINT_PROPERTY_EMAIL) 121 .setCardinality(AppSearchSchema.PropertyConfig.CARDINALITY_REPEATED) 122 .setIndexingType( 123 AppSearchSchema.StringPropertyConfig.INDEXING_TYPE_PREFIXES) 124 .setTokenizerType( 125 AppSearchSchema.StringPropertyConfig.TOKENIZER_TYPE_PLAIN) 126 .build()) 127 // telephone 128 .addProperty(new AppSearchSchema.StringPropertyConfig.Builder( 129 ContactPoint.CONTACT_POINT_PROPERTY_TELEPHONE) 130 .setCardinality(AppSearchSchema.PropertyConfig.CARDINALITY_REPEATED) 131 .setIndexingType( 132 AppSearchSchema.StringPropertyConfig.INDEXING_TYPE_PREFIXES) 133 .setTokenizerType( 134 AppSearchSchema.StringPropertyConfig.TOKENIZER_TYPE_PLAIN) 135 .build()) 136 .build(); 137 138 @NonNull getDocsByIdAsync( @onNull AppSearchSession session, @NonNull Collection<String> ids, @NonNull Executor executor)139 public static CompletableFuture<AppSearchBatchResult> getDocsByIdAsync( 140 @NonNull AppSearchSession session, @NonNull Collection<String> ids, 141 @NonNull Executor executor) { 142 Objects.requireNonNull(session); 143 Objects.requireNonNull(ids); 144 Objects.requireNonNull(executor); 145 146 CompletableFuture<AppSearchBatchResult> future = new CompletableFuture<>(); 147 GetByDocumentIdRequest request = new GetByDocumentIdRequest.Builder( 148 AppSearchHelper.NAMESPACE_NAME).addIds(ids).build(); 149 session.getByDocumentId(request, executor, 150 new BatchResultCallback<String, GenericDocument>() { 151 @Override 152 public void onResult(AppSearchBatchResult<String, GenericDocument> result) { 153 future.complete(result); 154 } 155 156 @Override 157 public void onSystemError(Throwable throwable) { 158 future.completeExceptionally(throwable); 159 } 160 }); 161 162 return future; 163 } 164 165 @NonNull getNextPageAsync( @onNull AppSearchSession session, @NonNull SearchResults searchResults, @NonNull Executor executor)166 public static CompletableFuture<List<SearchResult>> getNextPageAsync( 167 @NonNull AppSearchSession session, 168 @NonNull SearchResults searchResults, 169 @NonNull Executor executor) { 170 Objects.requireNonNull(searchResults); 171 Objects.requireNonNull(executor); 172 173 CompletableFuture<List<SearchResult>> future = new CompletableFuture<>(); 174 searchResults.getNextPage(executor, result -> { 175 if (result.isSuccess()) { 176 future.complete(result.getResultValue()); 177 } else { 178 future.completeExceptionally( 179 new AppSearchException(result.getResultCode(), result.getErrorMessage())); 180 } 181 }); 182 183 return future; 184 } 185 186 @NonNull getDocIdsByQuery(@onNull AppSearchSession session, @NonNull String query, @NonNull SearchSpec spec, @NonNull Executor executor)187 public static Set<String> getDocIdsByQuery(@NonNull AppSearchSession session, 188 @NonNull String query, 189 @NonNull SearchSpec spec, 190 @NonNull Executor executor) { 191 Objects.requireNonNull(session); 192 Objects.requireNonNull(query); 193 Objects.requireNonNull(spec); 194 Objects.requireNonNull(executor); 195 196 SearchResults results = session.search(query, spec); 197 Set<String> allIds = new ArraySet<>(); 198 try { 199 while (true) { 200 List<SearchResult> docs = getNextPageAsync(session, results, executor).get(); 201 if (docs.isEmpty()) { 202 break; 203 } 204 for (int i = 0; i < docs.size(); ++i) { 205 allIds.add(docs.get(i).getGenericDocument().getId()); 206 } 207 } 208 } catch (InterruptedException | ExecutionException | IllegalStateException e) { 209 // do nothing. 210 } 211 212 return allIds; 213 } 214 } 215