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