1 /* 2 * Copyright (C) 2023 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.systemui.log.echo 18 19 import android.util.Log 20 import com.android.systemui.log.core.LogLevel 21 import java.util.StringJoiner 22 23 /** 24 * Encodes/decodes the list of tags/buffers that [LogcatEchoTrackerDebug] echoes to logcat to/from a 25 * string format (that can be stored in a permanent place like a setting). 26 */ 27 class LogcatEchoSettingFormat { parseOverridesnull28 fun parseOverrides(str: String): List<LogcatEchoOverride> { 29 // The format begins with a schema version specifier formatted as "<number>;", followed by 30 // the encoded data. 31 32 // First, read the schema version: 33 val split = str.split(";", limit = 2) 34 if (split.size != 2) { 35 Log.e(TAG, "Unrecognized echo override format: \"$str\"") 36 return emptyList() 37 } 38 val formatVersion = 39 try { 40 split[0].toInt() 41 } catch (e: NumberFormatException) { 42 Log.e(TAG, "Unrecognized echo override formation version: ${split[0]}") 43 return emptyList() 44 } 45 46 // Then, dispatch to the appropriate parser based on format 47 return when (formatVersion) { 48 0 -> parseOverridesV0(split[1]) 49 else -> { 50 Log.e(TAG, "Unrecognized echo override formation version: $formatVersion") 51 emptyList() 52 } 53 } 54 } 55 stringifyOverridesnull56 fun stringifyOverrides( 57 overrides: List<LogcatEchoOverride>, 58 ): String { 59 return stringifyOverridesV0(overrides) 60 } 61 parseOverridesV0null62 private fun parseOverridesV0( 63 str: String, 64 ): List<LogcatEchoOverride> { 65 // Format: <type>;<name>;<level>(;...) 66 // Where 67 // <type> = "b" | "t" 68 // <name> = string 69 // <level> = "v" | "d" | "i" | "w" | "e" | "!" 70 71 val list = mutableListOf<LogcatEchoOverride>() 72 73 // Split on any ";" that is not preceded by a "\" 74 val pieces = str.split(Regex("""(?<!\\);""")) 75 76 var i = 0 77 while (i < pieces.size) { 78 if (pieces.size - i < 3) { 79 break 80 } 81 val type = 82 when (pieces[i]) { 83 "b" -> EchoOverrideType.BUFFER 84 "t" -> EchoOverrideType.TAG 85 else -> break 86 } 87 val name = pieces[i + 1].replace("\\;", ";") 88 val level = 89 when (pieces[i + 2]) { 90 "v" -> LogLevel.VERBOSE 91 "d" -> LogLevel.DEBUG 92 "i" -> LogLevel.INFO 93 "w" -> LogLevel.WARNING 94 "e" -> LogLevel.ERROR 95 "!" -> LogLevel.WTF 96 else -> break 97 } 98 i += 3 99 100 list.add(LogcatEchoOverride(type, name, level)) 101 } 102 103 return list 104 } 105 stringifyOverridesV0null106 private fun stringifyOverridesV0( 107 overrides: List<LogcatEchoOverride>, 108 ): String { 109 val sj = StringJoiner(";") 110 111 sj.add("0") 112 113 for (override in overrides) { 114 sj.add( 115 when (override.type) { 116 EchoOverrideType.BUFFER -> "b" 117 EchoOverrideType.TAG -> "t" 118 } 119 ) 120 sj.add(override.name.replace(";", "\\;")) 121 sj.add( 122 when (override.level) { 123 LogLevel.VERBOSE -> "v" 124 LogLevel.DEBUG -> "d" 125 LogLevel.INFO -> "i" 126 LogLevel.WARNING -> "w" 127 LogLevel.ERROR -> "e" 128 LogLevel.WTF -> "!" 129 } 130 ) 131 } 132 133 return sj.toString() 134 } 135 } 136 137 data class LogcatEchoOverride(val type: EchoOverrideType, val name: String, val level: LogLevel) 138 139 private const val TAG = "EchoFormat" 140