1 /*
2  * Copyright 2023 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;
18 
19 import android.annotation.FlaggedApi;
20 import android.annotation.NonNull;
21 import android.annotation.Nullable;
22 import android.app.appsearch.annotation.CanIgnoreReturnValue;
23 import android.app.appsearch.safeparcel.AbstractSafeParcelable;
24 import android.app.appsearch.safeparcel.SafeParcelable;
25 import android.os.Parcel;
26 import android.os.Parcelable;
27 import android.util.ArraySet;
28 
29 import com.android.appsearch.flags.Flags;
30 
31 import java.util.ArrayList;
32 import java.util.List;
33 import java.util.Map;
34 import java.util.Objects;
35 import java.util.Set;
36 
37 /**
38  * An expanded version of {@link SchemaVisibilityConfig} which includes fields for internal use by
39  * AppSearch.
40  *
41  * @hide
42  */
43 @SafeParcelable.Class(creator = "InternalVisibilityConfigCreator")
44 public final class InternalVisibilityConfig extends AbstractSafeParcelable {
45     @NonNull
46     public static final Parcelable.Creator<InternalVisibilityConfig> CREATOR =
47             new InternalVisibilityConfigCreator();
48 
49     /** Build the List of {@link InternalVisibilityConfig}s from visibility settings. */
50     @NonNull
toInternalVisibilityConfigs( @onNull SetSchemaRequest setSchemaRequest)51     public static List<InternalVisibilityConfig> toInternalVisibilityConfigs(
52             @NonNull SetSchemaRequest setSchemaRequest) {
53         Set<AppSearchSchema> searchSchemas = setSchemaRequest.getSchemas();
54         Set<String> schemasNotDisplayedBySystem = setSchemaRequest.getSchemasNotDisplayedBySystem();
55         Map<String, Set<PackageIdentifier>> schemasVisibleToPackages =
56                 setSchemaRequest.getSchemasVisibleToPackages();
57         Map<String, Set<Set<Integer>>> schemasVisibleToPermissions =
58                 setSchemaRequest.getRequiredPermissionsForSchemaTypeVisibility();
59         Map<String, PackageIdentifier> publiclyVisibleSchemas =
60                 setSchemaRequest.getPubliclyVisibleSchemas();
61         Map<String, Set<SchemaVisibilityConfig>> schemasVisibleToConfigs =
62                 setSchemaRequest.getSchemasVisibleToConfigs();
63 
64         List<InternalVisibilityConfig> result = new ArrayList<>(searchSchemas.size());
65         for (AppSearchSchema searchSchema : searchSchemas) {
66             String schemaType = searchSchema.getSchemaType();
67             InternalVisibilityConfig.Builder builder =
68                     new InternalVisibilityConfig.Builder(schemaType)
69                             .setNotDisplayedBySystem(
70                                     schemasNotDisplayedBySystem.contains(schemaType));
71 
72             Set<PackageIdentifier> visibleToPackages = schemasVisibleToPackages.get(schemaType);
73             if (visibleToPackages != null) {
74                 for (PackageIdentifier packageIdentifier : visibleToPackages) {
75                     builder.addVisibleToPackage(packageIdentifier);
76                 }
77             }
78 
79             Set<Set<Integer>> visibleToPermissionSets = schemasVisibleToPermissions.get(schemaType);
80             if (visibleToPermissionSets != null) {
81                 for (Set<Integer> visibleToPermissions : visibleToPermissionSets) {
82                     builder.addVisibleToPermissions(visibleToPermissions);
83                 }
84             }
85 
86             PackageIdentifier publiclyVisibleTargetPackage = publiclyVisibleSchemas.get(schemaType);
87             if (publiclyVisibleTargetPackage != null) {
88                 builder.setPubliclyVisibleTargetPackage(publiclyVisibleTargetPackage);
89             }
90 
91             Set<SchemaVisibilityConfig> visibleToConfigs = schemasVisibleToConfigs.get(schemaType);
92             if (visibleToConfigs != null) {
93                 for (SchemaVisibilityConfig schemaVisibilityConfig : visibleToConfigs) {
94                     builder.addVisibleToConfig(schemaVisibilityConfig);
95                 }
96             }
97 
98             result.add(builder.build());
99         }
100         return result;
101     }
102 
103     @NonNull
104     @Field(id = 1, getter = "getSchemaType")
105     private final String mSchemaType;
106 
107     @Field(id = 2, getter = "isNotDisplayedBySystem")
108     private final boolean mIsNotDisplayedBySystem;
109 
110     /** The public visibility settings available in VisibilityConfig. */
111     @NonNull
112     @Field(id = 3, getter = "getVisibilityConfig")
113     private final SchemaVisibilityConfig mVisibilityConfig;
114 
115     /** Extended visibility settings from {@link SetSchemaRequest#getSchemasVisibleToConfigs()} */
116     @NonNull
117     @Field(id = 4)
118     final List<SchemaVisibilityConfig> mVisibleToConfigs;
119 
120     @Constructor
InternalVisibilityConfig( @aramid = 1) @onNull String schemaType, @Param(id = 2) boolean isNotDisplayedBySystem, @Param(id = 3) @NonNull SchemaVisibilityConfig schemaVisibilityConfig, @Param(id = 4) @NonNull List<SchemaVisibilityConfig> visibleToConfigs)121     InternalVisibilityConfig(
122             @Param(id = 1) @NonNull String schemaType,
123             @Param(id = 2) boolean isNotDisplayedBySystem,
124             @Param(id = 3) @NonNull SchemaVisibilityConfig schemaVisibilityConfig,
125             @Param(id = 4) @NonNull List<SchemaVisibilityConfig> visibleToConfigs) {
126         mIsNotDisplayedBySystem = isNotDisplayedBySystem;
127         mSchemaType = Objects.requireNonNull(schemaType);
128         mVisibilityConfig = Objects.requireNonNull(schemaVisibilityConfig);
129         mVisibleToConfigs = Objects.requireNonNull(visibleToConfigs);
130     }
131 
132     /**
133      * Gets the schemaType for this VisibilityConfig.
134      *
135      * <p>This is being used as the document id when we convert a {@link InternalVisibilityConfig}
136      * to a {@link GenericDocument}.
137      */
138     @NonNull
getSchemaType()139     public String getSchemaType() {
140         return mSchemaType;
141     }
142 
143     /** Returns whether this schema is visible to the system. */
isNotDisplayedBySystem()144     public boolean isNotDisplayedBySystem() {
145         return mIsNotDisplayedBySystem;
146     }
147 
148     /**
149      * Returns the visibility settings stored in the public {@link SchemaVisibilityConfig} object.
150      */
151     @NonNull
getVisibilityConfig()152     public SchemaVisibilityConfig getVisibilityConfig() {
153         return mVisibilityConfig;
154     }
155 
156     /**
157      * Returns required {@link SchemaVisibilityConfig} sets for a caller need to match to access the
158      * schema this {@link InternalVisibilityConfig} represents.
159      */
160     @NonNull
getVisibleToConfigs()161     public Set<SchemaVisibilityConfig> getVisibleToConfigs() {
162         return new ArraySet<>(mVisibleToConfigs);
163     }
164 
165     @Override
writeToParcel(@onNull Parcel dest, int flags)166     public void writeToParcel(@NonNull Parcel dest, int flags) {
167         InternalVisibilityConfigCreator.writeToParcel(this, dest, flags);
168     }
169 
170     @Override
equals(@ullable Object o)171     public boolean equals(@Nullable Object o) {
172         if (this == o) {
173             return true;
174         }
175         if (o == null) {
176             return false;
177         }
178         if (!(o instanceof InternalVisibilityConfig)) {
179             return false;
180         }
181         InternalVisibilityConfig that = (InternalVisibilityConfig) o;
182         return mIsNotDisplayedBySystem == that.mIsNotDisplayedBySystem
183                 && Objects.equals(mSchemaType, that.mSchemaType)
184                 && Objects.equals(mVisibilityConfig, that.mVisibilityConfig)
185                 && Objects.equals(mVisibleToConfigs, that.mVisibleToConfigs);
186     }
187 
188     @Override
hashCode()189     public int hashCode() {
190         return Objects.hash(
191                 mIsNotDisplayedBySystem, mSchemaType, mVisibilityConfig, mVisibleToConfigs);
192     }
193 
194     /** The builder class of {@link InternalVisibilityConfig}. */
195     @FlaggedApi(Flags.FLAG_ENABLE_SET_SCHEMA_VISIBLE_TO_CONFIGS)
196     public static final class Builder {
197         private String mSchemaType;
198         private boolean mIsNotDisplayedBySystem;
199         private SchemaVisibilityConfig.Builder mVisibilityConfigBuilder;
200         private List<SchemaVisibilityConfig> mVisibleToConfigs = new ArrayList<>();
201         private boolean mBuilt;
202 
203         /**
204          * Creates a {@link Builder} for a {@link InternalVisibilityConfig}.
205          *
206          * @param schemaType The SchemaType of the {@link AppSearchSchema} that this {@link
207          *     InternalVisibilityConfig} represents. The package and database prefix will be added
208          *     in server side. We are using prefixed schema type to be the final id of this {@link
209          *     InternalVisibilityConfig}. This will be used as as an AppSearch id.
210          * @see GenericDocument#getId
211          */
Builder(@onNull String schemaType)212         public Builder(@NonNull String schemaType) {
213             mSchemaType = Objects.requireNonNull(schemaType);
214             mVisibilityConfigBuilder = new SchemaVisibilityConfig.Builder();
215         }
216 
217         /** Creates a {@link Builder} from an existing {@link InternalVisibilityConfig} */
Builder(@onNull InternalVisibilityConfig internalVisibilityConfig)218         public Builder(@NonNull InternalVisibilityConfig internalVisibilityConfig) {
219             Objects.requireNonNull(internalVisibilityConfig);
220             mSchemaType = internalVisibilityConfig.mSchemaType;
221             mIsNotDisplayedBySystem = internalVisibilityConfig.mIsNotDisplayedBySystem;
222             mVisibilityConfigBuilder =
223                     new SchemaVisibilityConfig.Builder(
224                             internalVisibilityConfig.getVisibilityConfig());
225             mVisibleToConfigs = internalVisibilityConfig.mVisibleToConfigs;
226         }
227 
228         /** Sets schemaType, which will be as the id when converting to {@link GenericDocument}. */
229         @NonNull
230         @CanIgnoreReturnValue
setSchemaType(@onNull String schemaType)231         public Builder setSchemaType(@NonNull String schemaType) {
232             resetIfBuilt();
233             mSchemaType = Objects.requireNonNull(schemaType);
234             return this;
235         }
236 
237         /**
238          * Resets all values contained in the VisibilityConfig with the values from the given
239          * VisibiltiyConfig.
240          */
241         @NonNull
242         @CanIgnoreReturnValue
setVisibilityConfig(@onNull SchemaVisibilityConfig schemaVisibilityConfig)243         public Builder setVisibilityConfig(@NonNull SchemaVisibilityConfig schemaVisibilityConfig) {
244             resetIfBuilt();
245             mVisibilityConfigBuilder = new SchemaVisibilityConfig.Builder(schemaVisibilityConfig);
246             return this;
247         }
248 
249         /** Sets whether this schema has opted out of platform surfacing. */
250         @CanIgnoreReturnValue
251         @NonNull
setNotDisplayedBySystem(boolean notDisplayedBySystem)252         public Builder setNotDisplayedBySystem(boolean notDisplayedBySystem) {
253             resetIfBuilt();
254             mIsNotDisplayedBySystem = notDisplayedBySystem;
255             return this;
256         }
257 
258         /**
259          * Add {@link PackageIdentifier} of packages which has access to this schema.
260          *
261          * @see SchemaVisibilityConfig.Builder#addAllowedPackage
262          */
263         @CanIgnoreReturnValue
264         @NonNull
addVisibleToPackage(@onNull PackageIdentifier packageIdentifier)265         public Builder addVisibleToPackage(@NonNull PackageIdentifier packageIdentifier) {
266             resetIfBuilt();
267             mVisibilityConfigBuilder.addAllowedPackage(packageIdentifier);
268             return this;
269         }
270 
271         /**
272          * Clears the list of packages which have access to this schema.
273          *
274          * @see SchemaVisibilityConfig.Builder#clearAllowedPackages
275          */
276         @CanIgnoreReturnValue
277         @NonNull
clearVisibleToPackages()278         public Builder clearVisibleToPackages() {
279             resetIfBuilt();
280             mVisibilityConfigBuilder.clearAllowedPackages();
281             return this;
282         }
283 
284         /**
285          * Adds a set of required Android {@link android.Manifest.permission} combination a package
286          * needs to hold to access the schema.
287          *
288          * @see SchemaVisibilityConfig.Builder#addRequiredPermissions
289          */
290         @CanIgnoreReturnValue
291         @NonNull
addVisibleToPermissions(@onNull Set<Integer> visibleToPermissions)292         public Builder addVisibleToPermissions(@NonNull Set<Integer> visibleToPermissions) {
293             resetIfBuilt();
294             mVisibilityConfigBuilder.addRequiredPermissions(visibleToPermissions);
295             return this;
296         }
297 
298         /**
299          * Clears all required permissions combinations set to this {@link SchemaVisibilityConfig}.
300          *
301          * @see SchemaVisibilityConfig.Builder#clearRequiredPermissions
302          */
303         @CanIgnoreReturnValue
304         @NonNull
clearVisibleToPermissions()305         public Builder clearVisibleToPermissions() {
306             resetIfBuilt();
307             mVisibilityConfigBuilder.clearRequiredPermissions();
308             return this;
309         }
310 
311         /**
312          * Specify that this schema should be publicly available, to the same packages that have
313          * visibility to the package passed as a parameter. This visibility is determined by the
314          * result of {@link android.content.pm.PackageManager#canPackageQuery}.
315          *
316          * @see SchemaVisibilityConfig.Builder#setPubliclyVisibleTargetPackage
317          */
318         @CanIgnoreReturnValue
319         @NonNull
setPubliclyVisibleTargetPackage( @ullable PackageIdentifier packageIdentifier)320         public Builder setPubliclyVisibleTargetPackage(
321                 @Nullable PackageIdentifier packageIdentifier) {
322             resetIfBuilt();
323             mVisibilityConfigBuilder.setPubliclyVisibleTargetPackage(packageIdentifier);
324             return this;
325         }
326 
327         /**
328          * Add the {@link SchemaVisibilityConfig} for a caller need to match to access the schema
329          * this {@link InternalVisibilityConfig} represents.
330          *
331          * <p>You can call this method repeatedly to add multiple {@link SchemaVisibilityConfig},
332          * and the querier will have access if they match ANY of the {@link SchemaVisibilityConfig}.
333          *
334          * @param schemaVisibilityConfig The {@link SchemaVisibilityConfig} hold all requirements
335          *     that a call must match to access the schema.
336          */
337         @CanIgnoreReturnValue
338         @NonNull
addVisibleToConfig(@onNull SchemaVisibilityConfig schemaVisibilityConfig)339         public Builder addVisibleToConfig(@NonNull SchemaVisibilityConfig schemaVisibilityConfig) {
340             Objects.requireNonNull(schemaVisibilityConfig);
341             resetIfBuilt();
342             mVisibleToConfigs.add(schemaVisibilityConfig);
343             return this;
344         }
345 
346         /** Clears the set of {@link SchemaVisibilityConfig} which have access to this schema. */
347         @CanIgnoreReturnValue
348         @NonNull
clearVisibleToConfig()349         public Builder clearVisibleToConfig() {
350             resetIfBuilt();
351             mVisibleToConfigs.clear();
352             return this;
353         }
354 
resetIfBuilt()355         private void resetIfBuilt() {
356             if (mBuilt) {
357                 mVisibleToConfigs = new ArrayList<>(mVisibleToConfigs);
358                 mBuilt = false;
359             }
360         }
361 
362         /** Build a {@link InternalVisibilityConfig} */
363         @NonNull
build()364         public InternalVisibilityConfig build() {
365             mBuilt = true;
366             return new InternalVisibilityConfig(
367                     mSchemaType,
368                     mIsNotDisplayedBySystem,
369                     mVisibilityConfigBuilder.build(),
370                     mVisibleToConfigs);
371         }
372     }
373 }
374