• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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.PackageIdentifierParcel;
25 import android.app.appsearch.safeparcel.SafeParcelable;
26 import android.os.Parcel;
27 import android.os.Parcelable;
28 import android.util.ArraySet;
29 
30 import com.android.appsearch.flags.Flags;
31 
32 import java.util.ArrayList;
33 import java.util.List;
34 import java.util.Objects;
35 import java.util.Set;
36 
37 /**
38  * A class to hold a all necessary Visibility information corresponding to the same schema. This
39  * pattern allows for easier association of these documents.
40  *
41  * <p>This does not correspond to any schema, the properties held in this class are kept in two
42  * separate schemas, VisibilityConfig and PublicAclOverlay.
43  */
44 @FlaggedApi(Flags.FLAG_ENABLE_SET_SCHEMA_VISIBLE_TO_CONFIGS)
45 @SafeParcelable.Class(creator = "VisibilityConfigCreator")
46 // TODO(b/384721898): Switch to JSpecify annotations
47 @SuppressWarnings({"HiddenSuperclass", "JSpecifyNullness"})
48 public final class SchemaVisibilityConfig extends AbstractSafeParcelable {
49 
50     public static final @NonNull Parcelable.Creator<SchemaVisibilityConfig> CREATOR =
51             new VisibilityConfigCreator();
52 
53     @Field(id = 1)
54     final @NonNull List<PackageIdentifierParcel> mAllowedPackages;
55 
56     @Field(id = 2)
57     final @NonNull List<VisibilityPermissionConfig> mRequiredPermissions;
58 
59     @Field(id = 3)
60     final @Nullable PackageIdentifierParcel mPubliclyVisibleTargetPackage;
61 
62     private @Nullable Integer mHashCode;
63     private @Nullable List<PackageIdentifier> mAllowedPackagesCached;
64     private @Nullable Set<Set<Integer>> mRequiredPermissionsCached;
65 
66     @Constructor
SchemaVisibilityConfig( @aramid = 1) @onNull List<PackageIdentifierParcel> allowedPackages, @Param(id = 2) @NonNull List<VisibilityPermissionConfig> requiredPermissions, @Param(id = 3) @Nullable PackageIdentifierParcel publiclyVisibleTargetPackage)67     SchemaVisibilityConfig(
68             @Param(id = 1) @NonNull List<PackageIdentifierParcel> allowedPackages,
69             @Param(id = 2) @NonNull List<VisibilityPermissionConfig> requiredPermissions,
70             @Param(id = 3) @Nullable PackageIdentifierParcel publiclyVisibleTargetPackage) {
71         mAllowedPackages = Objects.requireNonNull(allowedPackages);
72         mRequiredPermissions = Objects.requireNonNull(requiredPermissions);
73         mPubliclyVisibleTargetPackage = publiclyVisibleTargetPackage;
74     }
75 
76     /** Returns a list of {@link PackageIdentifier}s of packages that can access this schema. */
getAllowedPackages()77     public @NonNull List<PackageIdentifier> getAllowedPackages() {
78         if (mAllowedPackagesCached == null) {
79             mAllowedPackagesCached = new ArrayList<>(mAllowedPackages.size());
80             for (int i = 0; i < mAllowedPackages.size(); i++) {
81                 mAllowedPackagesCached.add(new PackageIdentifier(mAllowedPackages.get(i)));
82             }
83         }
84         return mAllowedPackagesCached;
85     }
86 
87     /**
88      * Returns an array of Integers representing Android Permissions that the caller must hold to
89      * access the schema this {@link SchemaVisibilityConfig} represents.
90      *
91      * @see SetSchemaRequest.Builder#addRequiredPermissionsForSchemaTypeVisibility(String, Set)
92      */
getRequiredPermissions()93     public @NonNull Set<Set<Integer>> getRequiredPermissions() {
94         if (mRequiredPermissionsCached == null) {
95             mRequiredPermissionsCached = new ArraySet<>(mRequiredPermissions.size());
96             for (int i = 0; i < mRequiredPermissions.size(); i++) {
97                 VisibilityPermissionConfig permissionConfig = mRequiredPermissions.get(i);
98                 Set<Integer> requiredPermissions = permissionConfig.getAllRequiredPermissions();
99                 if (mRequiredPermissionsCached != null && requiredPermissions != null) {
100                     mRequiredPermissionsCached.add(requiredPermissions);
101                 }
102             }
103         }
104         // Added for nullness checker as it is @Nullable, we initialize it above if it is null.
105         return Objects.requireNonNull(mRequiredPermissionsCached);
106     }
107 
108     /**
109      * Returns the {@link PackageIdentifier} of the package that will be used as the target package
110      * in a call to {@link android.content.pm.PackageManager#canPackageQuery} to determine which
111      * packages can access this publicly visible schema. Returns null if the schema is not publicly
112      * visible.
113      */
getPubliclyVisibleTargetPackage()114     public @Nullable PackageIdentifier getPubliclyVisibleTargetPackage() {
115         if (mPubliclyVisibleTargetPackage == null) {
116             return null;
117         }
118         return new PackageIdentifier(mPubliclyVisibleTargetPackage);
119     }
120 
121     @Override
writeToParcel(@onNull Parcel dest, int flags)122     public void writeToParcel(@NonNull Parcel dest, int flags) {
123         VisibilityConfigCreator.writeToParcel(this, dest, flags);
124     }
125 
126     @Override
equals(@ullable Object o)127     public boolean equals(@Nullable Object o) {
128         if (this == o) {
129             return true;
130         }
131         if (o == null) {
132             return false;
133         }
134         if (!(o instanceof SchemaVisibilityConfig)) {
135             return false;
136         }
137         SchemaVisibilityConfig that = (SchemaVisibilityConfig) o;
138         return Objects.equals(mAllowedPackages, that.mAllowedPackages)
139                 && Objects.equals(mRequiredPermissions, that.mRequiredPermissions)
140                 && Objects.equals(
141                         mPubliclyVisibleTargetPackage, that.mPubliclyVisibleTargetPackage);
142     }
143 
144     @Override
hashCode()145     public int hashCode() {
146         if (mHashCode == null) {
147             mHashCode =
148                     Objects.hash(
149                             mAllowedPackages, mRequiredPermissions, mPubliclyVisibleTargetPackage);
150         }
151         return mHashCode;
152     }
153 
154     /** The builder class of {@link SchemaVisibilityConfig}. */
155     @FlaggedApi(Flags.FLAG_ENABLE_SET_SCHEMA_VISIBLE_TO_CONFIGS)
156     public static final class Builder {
157         private List<PackageIdentifierParcel> mAllowedPackages = new ArrayList<>();
158         private List<VisibilityPermissionConfig> mRequiredPermissions = new ArrayList<>();
159         private @Nullable PackageIdentifierParcel mPubliclyVisibleTargetPackage;
160         private boolean mBuilt;
161 
162         /** Creates a {@link Builder} for a {@link SchemaVisibilityConfig}. */
Builder()163         public Builder() {}
164 
165         /**
166          * Creates a {@link Builder} copying the values from an existing {@link
167          * SchemaVisibilityConfig}.
168          *
169          * @hide
170          */
Builder(@onNull SchemaVisibilityConfig schemaVisibilityConfig)171         public Builder(@NonNull SchemaVisibilityConfig schemaVisibilityConfig) {
172             Objects.requireNonNull(schemaVisibilityConfig);
173             mAllowedPackages = new ArrayList<>(schemaVisibilityConfig.mAllowedPackages);
174             mRequiredPermissions = new ArrayList<>(schemaVisibilityConfig.mRequiredPermissions);
175             mPubliclyVisibleTargetPackage = schemaVisibilityConfig.mPubliclyVisibleTargetPackage;
176         }
177 
178         /** Add {@link PackageIdentifier} of packages which has access to this schema. */
179         @CanIgnoreReturnValue
addAllowedPackage(@onNull PackageIdentifier packageIdentifier)180         public @NonNull Builder addAllowedPackage(@NonNull PackageIdentifier packageIdentifier) {
181             Objects.requireNonNull(packageIdentifier);
182             resetIfBuilt();
183             mAllowedPackages.add(packageIdentifier.getPackageIdentifierParcel());
184             return this;
185         }
186 
187         /** Clears the list of packages which have access to this schema. */
188         @CanIgnoreReturnValue
clearAllowedPackages()189         public @NonNull Builder clearAllowedPackages() {
190             resetIfBuilt();
191             mAllowedPackages.clear();
192             return this;
193         }
194 
195         /**
196          * Adds a set of required Android {@link android.Manifest.permission} combination a package
197          * needs to hold to access the schema this {@link SchemaVisibilityConfig} represents.
198          *
199          * <p>If the querier holds ALL of the required permissions in this combination, they will
200          * have access to read {@link GenericDocument} objects of the given schema type.
201          *
202          * <p>You can call this method repeatedly to add multiple permission combinations, and the
203          * querier will have access if they holds ANY of the combinations.
204          *
205          * <p>Merged Set available from {@link #getRequiredPermissions()}.
206          *
207          * @see SetSchemaRequest.Builder#addRequiredPermissionsForSchemaTypeVisibility for supported
208          *     Permissions.
209          */
210         @SuppressWarnings("RequiresPermission") // No permission required to call this method
211         @CanIgnoreReturnValue
addRequiredPermissions(@onNull Set<Integer> visibleToPermissions)212         public @NonNull Builder addRequiredPermissions(@NonNull Set<Integer> visibleToPermissions) {
213             Objects.requireNonNull(visibleToPermissions);
214             resetIfBuilt();
215             mRequiredPermissions.add(new VisibilityPermissionConfig(visibleToPermissions));
216             return this;
217         }
218 
219         /**
220          * Clears all required permissions combinations set to this {@link SchemaVisibilityConfig}.
221          */
222         @CanIgnoreReturnValue
clearRequiredPermissions()223         public @NonNull Builder clearRequiredPermissions() {
224             resetIfBuilt();
225             mRequiredPermissions.clear();
226             return this;
227         }
228 
229         /**
230          * Specify that this schema should be publicly available, to the same packages that have
231          * visibility to the package passed as a parameter. This visibility is determined by the
232          * result of {@link android.content.pm.PackageManager#canPackageQuery}.
233          *
234          * <p>It is possible for the packageIdentifier parameter to be different from the package
235          * performing the indexing. This might happen in the case of an on-device indexer processing
236          * information about various packages. The visibility will be the same regardless of which
237          * package indexes the document, as the visibility is based on the packageIdentifier
238          * parameter.
239          *
240          * <p>Calling this with packageIdentifier set to null is valid, and will remove public
241          * visibility for the schema.
242          *
243          * @param packageIdentifier the {@link PackageIdentifier} of the package that will be used
244          *     as the target package in a call to {@link
245          *     android.content.pm.PackageManager#canPackageQuery} to determine which packages can
246          *     access this publicly visible schema.
247          */
248         @CanIgnoreReturnValue
setPubliclyVisibleTargetPackage( @ullable PackageIdentifier packageIdentifier)249         public @NonNull Builder setPubliclyVisibleTargetPackage(
250                 @Nullable PackageIdentifier packageIdentifier) {
251             resetIfBuilt();
252             if (packageIdentifier == null) {
253                 mPubliclyVisibleTargetPackage = null;
254             } else {
255                 mPubliclyVisibleTargetPackage = packageIdentifier.getPackageIdentifierParcel();
256             }
257             return this;
258         }
259 
resetIfBuilt()260         private void resetIfBuilt() {
261             if (mBuilt) {
262                 mAllowedPackages = new ArrayList<>(mAllowedPackages);
263                 mRequiredPermissions = new ArrayList<>(mRequiredPermissions);
264                 mBuilt = false;
265             }
266         }
267 
268         /** Build a {@link SchemaVisibilityConfig} */
build()269         public @NonNull SchemaVisibilityConfig build() {
270             mBuilt = true;
271             return new SchemaVisibilityConfig(
272                     mAllowedPackages, mRequiredPermissions, mPubliclyVisibleTargetPackage);
273         }
274     }
275 }
276