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