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 package com.android.server.appsearch.external.localstorage.visibilitystore;
17 
18 import android.annotation.NonNull;
19 import android.annotation.Nullable;
20 import android.app.appsearch.AppSearchSchema;
21 import android.app.appsearch.GenericDocument;
22 import android.app.appsearch.PackageIdentifier;
23 import android.app.appsearch.annotation.CanIgnoreReturnValue;
24 import android.util.ArraySet;
25 
26 import java.util.Objects;
27 import java.util.Set;
28 
29 /**
30  * Holds the visibility settings in version 1 that apply to a schema type.
31  *
32  * @hide
33  */
34 class VisibilityDocumentV1 extends GenericDocument {
35     /** The Schema type for documents that hold AppSearch's metadata, e.g. visibility settings. */
36     static final String SCHEMA_TYPE = "VisibilityType";
37 
38     /** Namespace of documents that contain visibility settings */
39     static final String NAMESPACE = "";
40 
41     /**
42      * Property that holds the list of platform-hidden schemas, as part of the visibility settings.
43      */
44     private static final String NOT_DISPLAYED_BY_SYSTEM_PROPERTY = "notPlatformSurfaceable";
45 
46     /** Property that holds the package name that can access a schema. */
47     private static final String PACKAGE_NAME_PROPERTY = "packageName";
48 
49     /** Property that holds the SHA 256 certificate of the app that can access a schema. */
50     private static final String SHA_256_CERT_PROPERTY = "sha256Cert";
51 
52     /** Property that holds the role can access a schema. */
53     private static final String ROLE_PROPERTY = "role";
54 
55     /** Property that holds the required permissions to access the schema. */
56     private static final String PERMISSION_PROPERTY = "permission";
57 
58     /** Schema for the VisibilityStore's documents. */
59     static final AppSearchSchema SCHEMA =
60             new AppSearchSchema.Builder(SCHEMA_TYPE)
61                     .addProperty(
62                             new AppSearchSchema.BooleanPropertyConfig.Builder(
63                                             NOT_DISPLAYED_BY_SYSTEM_PROPERTY)
64                                     .setCardinality(
65                                             AppSearchSchema.PropertyConfig.CARDINALITY_OPTIONAL)
66                                     .build())
67                     .addProperty(
68                             new AppSearchSchema.StringPropertyConfig.Builder(PACKAGE_NAME_PROPERTY)
69                                     .setCardinality(
70                                             AppSearchSchema.PropertyConfig.CARDINALITY_REPEATED)
71                                     .build())
72                     .addProperty(
73                             new AppSearchSchema.BytesPropertyConfig.Builder(SHA_256_CERT_PROPERTY)
74                                     .setCardinality(
75                                             AppSearchSchema.PropertyConfig.CARDINALITY_REPEATED)
76                                     .build())
77                     .addProperty(
78                             new AppSearchSchema.LongPropertyConfig.Builder(ROLE_PROPERTY)
79                                     .setCardinality(
80                                             AppSearchSchema.PropertyConfig.CARDINALITY_REPEATED)
81                                     .build())
82                     .addProperty(
83                             new AppSearchSchema.LongPropertyConfig.Builder(PERMISSION_PROPERTY)
84                                     .setCardinality(
85                                             AppSearchSchema.PropertyConfig.CARDINALITY_REPEATED)
86                                     .build())
87                     .build();
88 
VisibilityDocumentV1(@onNull GenericDocument genericDocument)89     VisibilityDocumentV1(@NonNull GenericDocument genericDocument) {
90         super(genericDocument);
91     }
92 
93     /** Returns whether this schema is visible to the system. */
isNotDisplayedBySystem()94     boolean isNotDisplayedBySystem() {
95         return getPropertyBoolean(NOT_DISPLAYED_BY_SYSTEM_PROPERTY);
96     }
97 
98     /**
99      * Returns a package name array which could access this schema. Use {@link #getSha256Certs()} to
100      * get package's sha 256 certs. The same index of package names array and sha256Certs array
101      * represents same package.
102      */
103     @NonNull
getPackageNames()104     String[] getPackageNames() {
105         return Objects.requireNonNull(getPropertyStringArray(PACKAGE_NAME_PROPERTY));
106     }
107 
108     /**
109      * Returns a package sha256Certs array which could access this schema. Use {@link
110      * #getPackageNames()} to get package's name. The same index of package names array and
111      * sha256Certs array represents same package.
112      */
113     @NonNull
getSha256Certs()114     byte[][] getSha256Certs() {
115         return Objects.requireNonNull(getPropertyBytesArray(SHA_256_CERT_PROPERTY));
116     }
117 
118     /**
119      * Returns an array of Android Roles that have access to the schema this {@link
120      * VisibilityDocumentV1} represents.
121      */
122     @Nullable
getVisibleToRoles()123     Set<Integer> getVisibleToRoles() {
124         return toInts(getPropertyLongArray(ROLE_PROPERTY));
125     }
126 
127     /**
128      * Returns an array of Android Permissions that caller mush hold to access the schema this
129      * {@link VisibilityDocumentV1} represents.
130      */
131     @Nullable
getVisibleToPermissions()132     Set<Integer> getVisibleToPermissions() {
133         return toInts(getPropertyLongArray(PERMISSION_PROPERTY));
134     }
135 
136     /** Builder for {@link VisibilityDocumentV1}. */
137     static class Builder extends GenericDocument.Builder<Builder> {
138         private final Set<PackageIdentifier> mPackageIdentifiers = new ArraySet<>();
139 
140         /**
141          * Creates a {@link Builder} for a {@link VisibilityDocumentV1}.
142          *
143          * @param id The SchemaType of the {@link AppSearchSchema} that this {@link
144          *     VisibilityDocumentV1} represents. The package and database prefix will be added in
145          *     server side. We are using prefixed schema type to be the final id of this {@link
146          *     VisibilityDocumentV1}.
147          */
Builder(@onNull String id)148         Builder(@NonNull String id) {
149             super(NAMESPACE, id, SCHEMA_TYPE);
150         }
151 
152         /** Sets whether this schema has opted out of platform surfacing. */
153         @CanIgnoreReturnValue
154         @NonNull
setNotDisplayedBySystem(boolean notDisplayedBySystem)155         Builder setNotDisplayedBySystem(boolean notDisplayedBySystem) {
156             return setPropertyBoolean(NOT_DISPLAYED_BY_SYSTEM_PROPERTY, notDisplayedBySystem);
157         }
158 
159         /** Add {@link PackageIdentifier} of packages which has access to this schema. */
160         @CanIgnoreReturnValue
161         @NonNull
addVisibleToPackages(@onNull Set<PackageIdentifier> packageIdentifiers)162         Builder addVisibleToPackages(@NonNull Set<PackageIdentifier> packageIdentifiers) {
163             Objects.requireNonNull(packageIdentifiers);
164             mPackageIdentifiers.addAll(packageIdentifiers);
165             return this;
166         }
167 
168         /** Add {@link PackageIdentifier} of packages which has access to this schema. */
169         @CanIgnoreReturnValue
170         @NonNull
addVisibleToPackage(@onNull PackageIdentifier packageIdentifier)171         Builder addVisibleToPackage(@NonNull PackageIdentifier packageIdentifier) {
172             Objects.requireNonNull(packageIdentifier);
173             mPackageIdentifiers.add(packageIdentifier);
174             return this;
175         }
176 
177         /**
178          * Add a set of Android role that has access to the schema this {@link VisibilityDocumentV1}
179          * represents.
180          */
181         @CanIgnoreReturnValue
182         @NonNull
setVisibleToRoles(@onNull Set<Integer> visibleToRoles)183         Builder setVisibleToRoles(@NonNull Set<Integer> visibleToRoles) {
184             Objects.requireNonNull(visibleToRoles);
185             setPropertyLong(ROLE_PROPERTY, toLongs(visibleToRoles));
186             return this;
187         }
188 
189         /**
190          * Add a set of Android role that has access to the schema this {@link VisibilityDocumentV1}
191          * represents.
192          */
193         @CanIgnoreReturnValue
194         @NonNull
setVisibleToPermissions(@onNull Set<Integer> visibleToPermissions)195         Builder setVisibleToPermissions(@NonNull Set<Integer> visibleToPermissions) {
196             Objects.requireNonNull(visibleToPermissions);
197             setPropertyLong(PERMISSION_PROPERTY, toLongs(visibleToPermissions));
198             return this;
199         }
200 
201         /** Build a {@link VisibilityDocumentV1} */
202         @Override
203         @NonNull
build()204         public VisibilityDocumentV1 build() {
205             String[] packageNames = new String[mPackageIdentifiers.size()];
206             byte[][] sha256Certs = new byte[mPackageIdentifiers.size()][32];
207             int i = 0;
208             for (PackageIdentifier packageIdentifier : mPackageIdentifiers) {
209                 packageNames[i] = packageIdentifier.getPackageName();
210                 sha256Certs[i] = packageIdentifier.getSha256Certificate();
211                 ++i;
212             }
213             setPropertyString(PACKAGE_NAME_PROPERTY, packageNames);
214             setPropertyBytes(SHA_256_CERT_PROPERTY, sha256Certs);
215             return new VisibilityDocumentV1(super.build());
216         }
217     }
218 
219     @NonNull
toLongs(@onNull Set<Integer> properties)220     static long[] toLongs(@NonNull Set<Integer> properties) {
221         long[] outputs = new long[properties.size()];
222         int i = 0;
223         for (int property : properties) {
224             outputs[i++] = property;
225         }
226         return outputs;
227     }
228 
229     @Nullable
toInts(@ullable long[] properties)230     private static Set<Integer> toInts(@Nullable long[] properties) {
231         if (properties == null) {
232             return null;
233         }
234         Set<Integer> outputs = new ArraySet<>(properties.length);
235         for (long property : properties) {
236             outputs.add((int) property);
237         }
238         return outputs;
239     }
240 }
241