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 androidx.appsearch.localstorage.visibilitystore;
18 
19 import androidx.annotation.RestrictTo;
20 import androidx.annotation.VisibleForTesting;
21 import androidx.appsearch.app.AppSearchResult;
22 import androidx.appsearch.app.InternalVisibilityConfig;
23 import androidx.appsearch.app.PackageIdentifier;
24 import androidx.appsearch.app.SetSchemaRequest;
25 import androidx.appsearch.exceptions.AppSearchException;
26 import androidx.appsearch.localstorage.AppSearchImpl;
27 import androidx.appsearch.localstorage.util.PrefixUtil;
28 import androidx.collection.ArraySet;
29 
30 import org.jspecify.annotations.NonNull;
31 
32 import java.util.ArrayList;
33 import java.util.Collections;
34 import java.util.List;
35 import java.util.Set;
36 
37 /**
38  * The helper class to store Visibility Document information of version 1 and handle the upgrade to
39  * latest version
40  *
41  * @exportToFramework:hide
42  */
43 @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
44 public class VisibilityStoreMigrationHelperFromV1 {
VisibilityStoreMigrationHelperFromV1()45     private VisibilityStoreMigrationHelperFromV1() {}
46 
47     /** Enum in {@link androidx.appsearch.app.SetSchemaRequest} AppSearch supported role. */
48     @VisibleForTesting
49     static final int DEPRECATED_ROLE_HOME = 1;
50 
51     /** Enum in {@link androidx.appsearch.app.SetSchemaRequest} AppSearch supported role. */
52     @VisibleForTesting
53     static final int DEPRECATED_ROLE_ASSISTANT = 2;
54 
55     /**  Reads all stored deprecated Visibility Document in version 0 from icing. */
getVisibilityDocumentsInVersion1( @onNull AppSearchImpl appSearchImpl)56     static List<VisibilityDocumentV1> getVisibilityDocumentsInVersion1(
57             @NonNull AppSearchImpl appSearchImpl) throws AppSearchException {
58         List<String> allPrefixedSchemaTypes = appSearchImpl.getAllPrefixedSchemaTypes();
59         List<VisibilityDocumentV1> visibilityDocumentV1s =
60                 new ArrayList<>(allPrefixedSchemaTypes.size());
61         for (int i = 0; i < allPrefixedSchemaTypes.size(); i++) {
62             String packageName = PrefixUtil.getPackageName(allPrefixedSchemaTypes.get(i));
63             if (packageName.equals(VisibilityStore.VISIBILITY_PACKAGE_NAME)) {
64                 continue; // Our own package. Skip.
65             }
66             try {
67                 // Note: We use the prefixed schema type as ids
68                 visibilityDocumentV1s.add(new VisibilityDocumentV1(appSearchImpl.getDocument(
69                         VisibilityStore.VISIBILITY_PACKAGE_NAME,
70                         VisibilityStore.DOCUMENT_VISIBILITY_DATABASE_NAME,
71                         VisibilityToDocumentConverter.VISIBILITY_DOCUMENT_NAMESPACE,
72                         allPrefixedSchemaTypes.get(i),
73                         /*typePropertyPaths=*/ Collections.emptyMap())));
74             } catch (AppSearchException e) {
75                 if (e.getResultCode() == AppSearchResult.RESULT_NOT_FOUND) {
76                     // TODO(b/172068212): This indicates some desync error. We were expecting a
77                     //  document, but didn't find one. Should probably reset AppSearch instead
78                     //  of ignoring it.
79                     continue;
80                 }
81                 // Otherwise, this is some other error we should pass up.
82                 throw e;
83             }
84         }
85         return visibilityDocumentV1s;
86     }
87 
88     /**
89      * Converts the given list of deprecated Visibility Documents into a Map of {@code
90      * <PrefixedSchemaType, VisibilityDocument.Builder of the latest version>}.
91      *
92      * @param visibilityDocumentV1s          The deprecated Visibility Document we found.
93      */
toVisibilityDocumentsV2( @onNull List<VisibilityDocumentV1> visibilityDocumentV1s)94     static @NonNull List<InternalVisibilityConfig> toVisibilityDocumentsV2(
95             @NonNull List<VisibilityDocumentV1> visibilityDocumentV1s) {
96         List<InternalVisibilityConfig> latestVisibilityDocuments =
97                 new ArrayList<>(visibilityDocumentV1s.size());
98         for (int i = 0; i < visibilityDocumentV1s.size(); i++) {
99             VisibilityDocumentV1 visibilityDocumentV1 = visibilityDocumentV1s.get(i);
100             Set<Set<Integer>> visibleToPermissionSets = new ArraySet<>();
101             Set<Integer> deprecatedVisibleToRoles = visibilityDocumentV1.getVisibleToRoles();
102             if (deprecatedVisibleToRoles != null) {
103                 for (int deprecatedVisibleToRole : deprecatedVisibleToRoles) {
104                     Set<Integer> visibleToPermission = new ArraySet<>();
105                     switch (deprecatedVisibleToRole) {
106                         case DEPRECATED_ROLE_HOME:
107                             visibleToPermission.add(SetSchemaRequest.READ_HOME_APP_SEARCH_DATA);
108                             break;
109                         case DEPRECATED_ROLE_ASSISTANT:
110                             visibleToPermission.add(SetSchemaRequest
111                                     .READ_ASSISTANT_APP_SEARCH_DATA);
112                             break;
113                     }
114                     visibleToPermissionSets.add(visibleToPermission);
115                 }
116             }
117             Set<Integer> deprecatedVisibleToPermissions =
118                     visibilityDocumentV1.getVisibleToPermissions();
119             if (deprecatedVisibleToPermissions != null) {
120                 visibleToPermissionSets.add(deprecatedVisibleToPermissions);
121             }
122 
123             InternalVisibilityConfig.Builder latestVisibilityDocumentBuilder =
124                     new InternalVisibilityConfig.Builder(visibilityDocumentV1.getId())
125                             .setNotDisplayedBySystem(visibilityDocumentV1.isNotDisplayedBySystem());
126             String[] packageNames = visibilityDocumentV1.getPackageNames();
127             byte[][] sha256Certs = visibilityDocumentV1.getSha256Certs();
128             if (packageNames.length == sha256Certs.length) {
129                 for (int j = 0; j < packageNames.length; j++) {
130                     latestVisibilityDocumentBuilder.addVisibleToPackage(
131                             new PackageIdentifier(packageNames[j], sha256Certs[j]));
132                 }
133             }
134             for (Set<Integer> visibleToPermissions : visibleToPermissionSets) {
135                 latestVisibilityDocumentBuilder.addVisibleToPermissions(visibleToPermissions);
136             }
137             latestVisibilityDocuments.add(latestVisibilityDocumentBuilder.build());
138         }
139         return latestVisibilityDocuments;
140     }
141 }
142