/* * Copyright (C) 2022 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.server.appsearch.util; import android.annotation.NonNull; import android.util.Log; import com.google.android.icing.proto.DebugInfoProto; import com.google.android.icing.proto.DocumentDebugInfoProto; import com.google.android.icing.proto.DocumentStorageInfoProto; import com.google.android.icing.proto.NamespaceStorageInfoProto; import com.google.android.icing.proto.PropertyConfigProto; import com.google.android.icing.proto.SchemaDebugInfoProto; import com.google.android.icing.proto.SchemaProto; import com.google.android.icing.proto.SchemaTypeConfigProto; import java.nio.charset.StandardCharsets; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; import java.util.Objects; /** A utility class for helper methods to process {@link DebugInfoProto}. */ public final class AdbDumpUtil { private static final String TAG = "AppSearchAdbDumpUtil"; private static final char[] HEX_ARRAY = "0123456789ABCDEF".toCharArray(); /** * Generate MD5 hash to help anonymize some string fields in {@link DebugInfoProto}. * * @param str The original string. * @return The hash value of str in hex format. */ @NonNull public static String generateFingerprintMd5(@NonNull String str) { Objects.requireNonNull(str); try { MessageDigest md = MessageDigest.getInstance("MD5"); md.update(str.getBytes(StandardCharsets.UTF_8)); byte[] bytes = md.digest(); char[] hexChars = new char[bytes.length * 2]; for (int i = 0; i < bytes.length; i++) { int v = bytes[i] & 0xFF; hexChars[i * 2] = HEX_ARRAY[v >>> 4]; hexChars[i * 2 + 1] = HEX_ARRAY[v & 0x0F]; } return new String(hexChars); } catch (NoSuchAlgorithmException e) { Log.e(TAG, "Failed to generate fingerprint"); } return ""; } /** * Anonymize some privacy-sensitive string fields in {@link DebugInfoProto}. * * @param debugInfo The original {@link DebugInfoProto} to be desensitized. * @return The desensitized {@link DebugInfoProto}. */ @NonNull public static DebugInfoProto desensitizeDebugInfo(@NonNull DebugInfoProto debugInfo) { Objects.requireNonNull(debugInfo); DebugInfoProto.Builder debugInfoBuilder = debugInfo.toBuilder(); DocumentDebugInfoProto.Builder documentInfoBuilder = debugInfoBuilder.getDocumentInfo().toBuilder(); for (int i = 0; i < documentInfoBuilder.getCorpusInfoCount(); ++i) { DocumentDebugInfoProto.CorpusInfo.Builder corpusInfoBuilder = documentInfoBuilder.getCorpusInfo(i).toBuilder(); corpusInfoBuilder.setNamespace( generateFingerprintMd5(corpusInfoBuilder.getNamespace())); corpusInfoBuilder.setSchema(generateFingerprintMd5(corpusInfoBuilder.getSchema())); documentInfoBuilder.setCorpusInfo(i, corpusInfoBuilder); } DocumentStorageInfoProto.Builder documentStorageInfoBuilder = documentInfoBuilder.getDocumentStorageInfo().toBuilder(); for (int i = 0; i < documentStorageInfoBuilder.getNamespaceStorageInfoCount(); ++i) { NamespaceStorageInfoProto.Builder namespaceStorageInfoBuilder = documentStorageInfoBuilder.getNamespaceStorageInfo(i).toBuilder(); namespaceStorageInfoBuilder.setNamespace( generateFingerprintMd5(namespaceStorageInfoBuilder.getNamespace())); documentStorageInfoBuilder.setNamespaceStorageInfo(i, namespaceStorageInfoBuilder); } documentInfoBuilder.setDocumentStorageInfo(documentStorageInfoBuilder); debugInfoBuilder.setDocumentInfo(documentInfoBuilder); SchemaDebugInfoProto.Builder schemaInfoBuilder = debugInfoBuilder.getSchemaInfo().toBuilder(); SchemaProto.Builder schemaBuilder = schemaInfoBuilder.getSchema().toBuilder(); for (int i = 0; i < schemaBuilder.getTypesCount(); ++i) { SchemaTypeConfigProto.Builder typeBuilder = schemaBuilder.getTypes(i).toBuilder(); typeBuilder.setSchemaType(generateFingerprintMd5(typeBuilder.getSchemaType())); for (int j = 0; j < typeBuilder.getPropertiesCount(); ++j) { PropertyConfigProto property = typeBuilder.getProperties(j); if (property.getDataType() == PropertyConfigProto.DataType.Code.DOCUMENT) { PropertyConfigProto.Builder propertyBuilder = property.toBuilder(); propertyBuilder.setSchemaType( generateFingerprintMd5(propertyBuilder.getSchemaType())); typeBuilder.setProperties(j, propertyBuilder); } } schemaBuilder.setTypes(i, typeBuilder); } schemaInfoBuilder.setSchema(schemaBuilder); debugInfoBuilder.setSchemaInfo(schemaInfoBuilder); return debugInfoBuilder.build(); } private AdbDumpUtil() {} }