1 /*
2  * Copyright (C) 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.util;
18 
19 import android.annotation.NonNull;
20 import android.util.Log;
21 
22 import com.google.android.icing.proto.DebugInfoProto;
23 import com.google.android.icing.proto.DocumentDebugInfoProto;
24 import com.google.android.icing.proto.DocumentStorageInfoProto;
25 import com.google.android.icing.proto.NamespaceStorageInfoProto;
26 import com.google.android.icing.proto.PropertyConfigProto;
27 import com.google.android.icing.proto.SchemaDebugInfoProto;
28 import com.google.android.icing.proto.SchemaProto;
29 import com.google.android.icing.proto.SchemaTypeConfigProto;
30 
31 import java.nio.charset.StandardCharsets;
32 import java.security.MessageDigest;
33 import java.security.NoSuchAlgorithmException;
34 import java.util.Objects;
35 
36 /** A utility class for helper methods to process {@link DebugInfoProto}. */
37 public final class AdbDumpUtil {
38     private static final String TAG = "AppSearchAdbDumpUtil";
39     private static final char[] HEX_ARRAY = "0123456789ABCDEF".toCharArray();
40 
41     /**
42      * Generate MD5 hash to help anonymize some string fields in {@link DebugInfoProto}.
43      *
44      * @param str The original string.
45      * @return The hash value of str in hex format.
46      */
47     @NonNull
generateFingerprintMd5(@onNull String str)48     public static String generateFingerprintMd5(@NonNull String str) {
49         Objects.requireNonNull(str);
50 
51         try {
52             MessageDigest md = MessageDigest.getInstance("MD5");
53             md.update(str.getBytes(StandardCharsets.UTF_8));
54             byte[] bytes = md.digest();
55             char[] hexChars = new char[bytes.length * 2];
56             for (int i = 0; i < bytes.length; i++) {
57                 int v = bytes[i] & 0xFF;
58                 hexChars[i * 2] = HEX_ARRAY[v >>> 4];
59                 hexChars[i * 2 + 1] = HEX_ARRAY[v & 0x0F];
60             }
61             return new String(hexChars);
62         } catch (NoSuchAlgorithmException e) {
63             Log.e(TAG, "Failed to generate fingerprint");
64         }
65         return "";
66     }
67 
68     /**
69      * Anonymize some privacy-sensitive string fields in {@link DebugInfoProto}.
70      *
71      * @param debugInfo The original {@link DebugInfoProto} to be desensitized.
72      * @return The desensitized {@link DebugInfoProto}.
73      */
74     @NonNull
desensitizeDebugInfo(@onNull DebugInfoProto debugInfo)75     public static DebugInfoProto desensitizeDebugInfo(@NonNull DebugInfoProto debugInfo) {
76         Objects.requireNonNull(debugInfo);
77 
78         DebugInfoProto.Builder debugInfoBuilder = debugInfo.toBuilder();
79         DocumentDebugInfoProto.Builder documentInfoBuilder =
80                 debugInfoBuilder.getDocumentInfo().toBuilder();
81 
82         for (int i = 0; i < documentInfoBuilder.getCorpusInfoCount(); ++i) {
83             DocumentDebugInfoProto.CorpusInfo.Builder corpusInfoBuilder =
84                     documentInfoBuilder.getCorpusInfo(i).toBuilder();
85             corpusInfoBuilder.setNamespace(
86                     generateFingerprintMd5(corpusInfoBuilder.getNamespace()));
87             corpusInfoBuilder.setSchema(generateFingerprintMd5(corpusInfoBuilder.getSchema()));
88             documentInfoBuilder.setCorpusInfo(i, corpusInfoBuilder);
89         }
90 
91         DocumentStorageInfoProto.Builder documentStorageInfoBuilder =
92                 documentInfoBuilder.getDocumentStorageInfo().toBuilder();
93         for (int i = 0; i < documentStorageInfoBuilder.getNamespaceStorageInfoCount(); ++i) {
94             NamespaceStorageInfoProto.Builder namespaceStorageInfoBuilder =
95                     documentStorageInfoBuilder.getNamespaceStorageInfo(i).toBuilder();
96             namespaceStorageInfoBuilder.setNamespace(
97                     generateFingerprintMd5(namespaceStorageInfoBuilder.getNamespace()));
98             documentStorageInfoBuilder.setNamespaceStorageInfo(i, namespaceStorageInfoBuilder);
99         }
100         documentInfoBuilder.setDocumentStorageInfo(documentStorageInfoBuilder);
101 
102         debugInfoBuilder.setDocumentInfo(documentInfoBuilder);
103 
104         SchemaDebugInfoProto.Builder schemaInfoBuilder =
105                 debugInfoBuilder.getSchemaInfo().toBuilder();
106         SchemaProto.Builder schemaBuilder = schemaInfoBuilder.getSchema().toBuilder();
107         for (int i = 0; i < schemaBuilder.getTypesCount(); ++i) {
108             SchemaTypeConfigProto.Builder typeBuilder = schemaBuilder.getTypes(i).toBuilder();
109             typeBuilder.setSchemaType(generateFingerprintMd5(typeBuilder.getSchemaType()));
110             for (int j = 0; j < typeBuilder.getPropertiesCount(); ++j) {
111                 PropertyConfigProto property = typeBuilder.getProperties(j);
112                 if (property.getDataType() == PropertyConfigProto.DataType.Code.DOCUMENT) {
113                     PropertyConfigProto.Builder propertyBuilder = property.toBuilder();
114                     propertyBuilder.setSchemaType(
115                             generateFingerprintMd5(propertyBuilder.getSchemaType()));
116                     typeBuilder.setProperties(j, propertyBuilder);
117                 }
118             }
119             schemaBuilder.setTypes(i, typeBuilder);
120         }
121         schemaInfoBuilder.setSchema(schemaBuilder);
122         debugInfoBuilder.setSchemaInfo(schemaInfoBuilder);
123         return debugInfoBuilder.build();
124     }
125 
AdbDumpUtil()126     private AdbDumpUtil() {}
127 }
128