1 /* 2 * Copyright 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.external.localstorage.converter; 18 19 import android.annotation.NonNull; 20 import android.util.ArraySet; 21 22 import com.google.android.icing.proto.SchemaTypeConfigProto; 23 24 import java.util.List; 25 import java.util.Map; 26 import java.util.Set; 27 28 /** 29 * Utilities for working with {@link SearchSpecToProtoConverter} and {@link 30 * SearchSuggestionSpecToProtoConverter}. 31 * 32 * @hide 33 */ 34 public class SearchSpecToProtoConverterUtil { SearchSpecToProtoConverterUtil()35 private SearchSpecToProtoConverterUtil() {} 36 37 /** 38 * Add prefix to the given namespace filters that user want to search over and find the 39 * intersection set with those prefixed namespace candidates that are stored in AppSearch. 40 * 41 * @param prefixes Set of database prefix which the caller want to access. 42 * @param namespaceMap The cached Map of {@code <Prefix, Set<PrefixedNamespace>>} stores all 43 * prefixed namespace filters which are stored in AppSearch. 44 * @param inputNamespaceFilters The set contains all desired but un-prefixed namespace filters 45 * of user. If the inputNamespaceFilters is empty, all existing prefixedCandidates will be 46 * added to the prefixedTargetFilters. 47 */ generateTargetNamespaceFilters( @onNull Set<String> prefixes, @NonNull Map<String, Set<String>> namespaceMap, @NonNull List<String> inputNamespaceFilters)48 static Set<String> generateTargetNamespaceFilters( 49 @NonNull Set<String> prefixes, 50 @NonNull Map<String, Set<String>> namespaceMap, 51 @NonNull List<String> inputNamespaceFilters) { 52 // Convert namespace filters to prefixed namespace filters 53 Set<String> targetPrefixedNamespaceFilters = new ArraySet<>(); 54 for (String prefix : prefixes) { 55 // Step1: find all prefixed namespace candidates that are stored in AppSearch. 56 Set<String> prefixedNamespaceCandidates = namespaceMap.get(prefix); 57 if (prefixedNamespaceCandidates == null) { 58 // This is should never happen. All prefixes should be verified before reach 59 // here. 60 continue; 61 } 62 // Step2: get the intersection of user searching filters and those candidates which are 63 // stored in AppSearch. 64 addIntersectedFilters( 65 prefix, 66 prefixedNamespaceCandidates, 67 inputNamespaceFilters, 68 targetPrefixedNamespaceFilters); 69 } 70 return targetPrefixedNamespaceFilters; 71 } 72 73 /** 74 * Add prefix to the given schema filters that user want to search over and find the 75 * intersection set with those prefixed schema candidates that are stored in AppSearch. 76 * 77 * @param prefixes Set of database prefix which the caller want to access. 78 * @param schemaMap The cached Map of {@code <Prefix, Map<PrefixedSchemaType, schemaProto>>} 79 * stores all prefixed schema filters which are stored in AppSearch. 80 * @param inputSchemaFilters The set contains all desired but un-prefixed namespace filters of 81 * user. If the inputSchemaFilters is empty, all existing prefixedCandidates will be added 82 * to the prefixedTargetFilters. 83 */ generateTargetSchemaFilters( @onNull Set<String> prefixes, @NonNull Map<String, Map<String, SchemaTypeConfigProto>> schemaMap, @NonNull List<String> inputSchemaFilters)84 static Set<String> generateTargetSchemaFilters( 85 @NonNull Set<String> prefixes, 86 @NonNull Map<String, Map<String, SchemaTypeConfigProto>> schemaMap, 87 @NonNull List<String> inputSchemaFilters) { 88 Set<String> targetPrefixedSchemaFilters = new ArraySet<>(); 89 // Append prefix to input schema filters and get the intersection of existing schema filter. 90 for (String prefix : prefixes) { 91 // Step1: find all prefixed schema candidates that are stored in AppSearch. 92 Map<String, SchemaTypeConfigProto> prefixedSchemaMap = schemaMap.get(prefix); 93 if (prefixedSchemaMap == null) { 94 // This is should never happen. All prefixes should be verified before reach 95 // here. 96 continue; 97 } 98 Set<String> prefixedSchemaCandidates = prefixedSchemaMap.keySet(); 99 // Step2: get the intersection of user searching filters and those candidates which are 100 // stored in AppSearch. 101 addIntersectedFilters( 102 prefix, 103 prefixedSchemaCandidates, 104 inputSchemaFilters, 105 targetPrefixedSchemaFilters); 106 } 107 return targetPrefixedSchemaFilters; 108 } 109 110 /** 111 * Find the intersection set of candidates existing in AppSearch and user specified filters. 112 * 113 * @param prefix The package and database's identifier. 114 * @param prefixedCandidates The set contains all prefixed candidates which are existing in a 115 * database. 116 * @param inputFilters The set contains all desired but un-prefixed filters of user. If the 117 * inputFilters is empty, all prefixedCandidates will be added to the prefixedTargetFilters. 118 * @param prefixedTargetFilters The output set contains all desired prefixed filters which are 119 * existing in the database. 120 */ addIntersectedFilters( @onNull String prefix, @NonNull Set<String> prefixedCandidates, @NonNull List<String> inputFilters, @NonNull Set<String> prefixedTargetFilters)121 private static void addIntersectedFilters( 122 @NonNull String prefix, 123 @NonNull Set<String> prefixedCandidates, 124 @NonNull List<String> inputFilters, 125 @NonNull Set<String> prefixedTargetFilters) { 126 if (inputFilters.isEmpty()) { 127 // Client didn't specify certain schemas to search over, add all candidates. 128 prefixedTargetFilters.addAll(prefixedCandidates); 129 } else { 130 // Client specified some filters to search over, check and only add those are 131 // existing in the database. 132 for (int i = 0; i < inputFilters.size(); i++) { 133 String prefixedTargetFilter = prefix + inputFilters.get(i); 134 if (prefixedCandidates.contains(prefixedTargetFilter)) { 135 prefixedTargetFilters.add(prefixedTargetFilter); 136 } 137 } 138 } 139 } 140 } 141