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.IndentingPrintWriter
20 import com.android.systemui.log.core.LogLevel
21 import com.android.systemui.log.echo.Outcome.Failure
22 import com.android.systemui.log.echo.Outcome.Success
23 import com.android.systemui.statusbar.commandline.ParseableCommand
24 import com.android.systemui.statusbar.commandline.Type
25 import java.io.PrintWriter
26
27 /**
28 * Implementation of command-line interface for modifying echo tracking.
29 *
30 * Invoked via $adb shell cmd statusbar echo <usage>. See [usage] below for usage summary.
31 */
32 internal class LogcatEchoTrackerCommand(private val echoTracker: LogcatEchoTrackerDebug) :
33 ParseableCommand(ECHO_TRACKER_COMMAND_NAME) {
34
35 val buffer by
36 param(
37 longName = "buffer",
38 shortName = "b",
39 description =
40 "Modifies the echo level of a buffer. Use the form <name>:<level>, e.g." +
41 " 'Foo:V'. Valid levels are V,D,I,W,E, and -. The - level clears any" +
42 " pre-existing override.",
43 valueParser = Type.String,
44 )
45
46 val tag by
47 param(
48 longName = "tag",
49 shortName = "t",
50 description =
51 "Modifies the echo level of a tag. Use the form <name>:<level>, e.g." +
52 " 'Foo:V'. Valid levels are V,D,I,W,E, and -. The - level clears any" +
53 " pre-existing override.",
54 valueParser = Type.String
55 )
56
57 val clearAll by
58 flag(
59 longName = "clear-all",
60 description = "Removes all local echo level overrides",
61 )
62
63 val list by
64 flag(
65 longName = "list",
66 description = "Lists all local echo level overrides",
67 )
68
usagenull69 override fun usage(pw: IndentingPrintWriter) {
70 pw.println("Usage:")
71 pw.println()
72 pw.println("echo -b MyBufferName:V // Set echo level of a buffer to verbose")
73 pw.println("echo -t MyTagName:V // Set echo level of a tag to verbose")
74 pw.println()
75 pw.println("echo -b MyBufferName:- // Clear any echo overrides for a buffer")
76 pw.println("echo -t MyTagName:- // Clear any echo overrides for a tag")
77 pw.println()
78 pw.println("echo --list // List all current echo overrides")
79 pw.println("echo --clear-all // Clear all echo overrides")
80 pw.println()
81 }
82
executenull83 override fun execute(pw: PrintWriter) {
84 val buffer = buffer
85 val tag = tag
86
87 when {
88 buffer != null -> {
89 parseTagStructure(buffer, EchoOverrideType.BUFFER).ifFailureThenPrintElse(pw) {
90 echoTracker.setEchoLevel(it.type, it.name, it.level)
91 }
92 }
93 tag != null -> {
94 parseTagStructure(tag, EchoOverrideType.TAG).ifFailureThenPrintElse(pw) {
95 echoTracker.setEchoLevel(it.type, it.name, it.level)
96 }
97 }
98 clearAll -> {
99 echoTracker.clearAllOverrides()
100 }
101 list -> {
102 for (override in echoTracker.listEchoOverrides()) {
103 pw.print(override.type.toString().padEnd(8))
104 pw.print(override.level.toString().padEnd(10))
105 pw.print(override.name)
106 pw.println()
107 }
108 }
109 else -> {
110 pw.println("You must specify one of --buffer, --tag, --list, or --clear-all")
111 }
112 }
113 }
114
parseTagStructurenull115 private fun parseTagStructure(
116 str: String,
117 type: EchoOverrideType,
118 ): Outcome<ParsedOverride> {
119 val result =
120 OVERRIDE_PATTERN.matchEntire(str)
121 ?: return Failure("Cannot parse override format, must be `<name>:<level>`")
122
123 val name = result.groupValues[1]
124 val levelStr = result.groupValues[2]
125
126 if (levelStr == "-") {
127 return Success(ParsedOverride(type, name, null))
128 } else {
129 val parsedLevel =
130 parseLevel(levelStr)
131 ?: return Failure("Unrecognized level $levelStr. Must be one of 'v,d,i,w,e,-'")
132 return Success(ParsedOverride(type, name, parsedLevel))
133 }
134 }
135
parseLevelnull136 private fun parseLevel(str: String): LogLevel? {
137 return when (str.lowercase()) {
138 "verbose" -> LogLevel.VERBOSE
139 "v" -> LogLevel.VERBOSE
140 "debug" -> LogLevel.DEBUG
141 "d" -> LogLevel.DEBUG
142 "info" -> LogLevel.INFO
143 "i" -> LogLevel.INFO
144 "warning" -> LogLevel.WARNING
145 "warn" -> LogLevel.WARNING
146 "w" -> LogLevel.WARNING
147 "error" -> LogLevel.ERROR
148 "e" -> LogLevel.ERROR
149 "assert" -> LogLevel.WTF
150 "wtf" -> LogLevel.WTF
151 else -> null
152 }
153 }
154
155 companion object {
156 const val ECHO_TRACKER_COMMAND_NAME = "echo"
157 }
158 }
159
160 private val OVERRIDE_PATTERN = Regex("([^:]+):(.*)")
161
162 private class ParsedOverride(val type: EchoOverrideType, val name: String, val level: LogLevel?)
163
164 private sealed interface Outcome<out T> {
165 class Success<out T>(val value: T) : Outcome<T>
166 class Failure(val message: String) : Outcome<Nothing>
167 }
168
ifFailureThenPrintElsenull169 private inline fun <T> Outcome<T>.ifFailureThenPrintElse(
170 pw: PrintWriter,
171 handler: (value: T) -> Unit,
172 ) {
173 when (this) {
174 is Success<T> -> handler(value)
175 is Failure -> pw.println(message)
176 }
177 }
178
179 /*
180 TODO (b/310006154): Investigate using varargs instead of parameterized flags
181
182 Current structure uses param flags, e.g.
183
184 adb shell cmd statusbar echo -b MyBufferName:V
185 adb shell cmd statusbar echo -b MyBufferName:-
186 adb shell cmd statusbar echo -t MyTagName:V
187 adb shell cmd statusbar echo -t MyTagName:-
188 adb shell cmd statusbar echo --clear-all
189 adb shell cmd statusbar echo --list
190
191 A better structure might use non-flag varargs like (but will require updates to the CLI lib):
192
193 adb shell cmd statusbar echo buffer MyBufferName:V
194 adb shell cmd statusbar echo buffer MyBufferName:-
195 adb shell cmd statusbar echo tag MyTagName:V
196 adb shell cmd statusbar echo tag MyTagName:-
197 adb shell cmd statusbar echo clear-all
198 adb shell cmd statusbar echo list
199
200 */
201