1 /* 2 * Copyright (C) 2020 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.plugins.log 18 19 import android.content.ContentResolver 20 import android.database.ContentObserver 21 import android.net.Uri 22 import android.os.Handler 23 import android.os.Looper 24 import android.os.Trace 25 import android.provider.Settings 26 27 /** 28 * Version of [LogcatEchoTracker] for debuggable builds 29 * 30 * The log level of individual buffers or tags can be controlled via global settings: 31 * ``` 32 * # Echo any message to <bufferName> of <level> or higher 33 * $ adb shell settings put global systemui/buffer/<bufferName> <level> 34 * 35 * # Echo any message of <tag> and of <level> or higher 36 * $ adb shell settings put global systemui/tag/<tag> <level> 37 * ``` 38 */ 39 class LogcatEchoTrackerDebug private constructor(private val contentResolver: ContentResolver) : 40 LogcatEchoTracker { 41 private val cachedBufferLevels: MutableMap<String, LogLevel> = mutableMapOf() 42 private val cachedTagLevels: MutableMap<String, LogLevel> = mutableMapOf() 43 override val logInBackgroundThread = true 44 45 companion object Factory { 46 @JvmStatic createnull47 fun create(contentResolver: ContentResolver, mainLooper: Looper): LogcatEchoTrackerDebug { 48 val tracker = LogcatEchoTrackerDebug(contentResolver) 49 tracker.attach(mainLooper) 50 return tracker 51 } 52 } 53 clearCachenull54 private fun clearCache() { 55 Trace.beginSection("LogcatEchoTrackerDebug#clearCache") 56 cachedBufferLevels.clear() 57 Trace.endSection() 58 } 59 attachnull60 private fun attach(mainLooper: Looper) { 61 Trace.beginSection("LogcatEchoTrackerDebug#attach") 62 contentResolver.registerContentObserver( 63 Settings.Global.getUriFor(BUFFER_PATH), 64 true, 65 object : ContentObserver(Handler(mainLooper)) { 66 override fun onChange(selfChange: Boolean, uri: Uri?) { 67 super.onChange(selfChange, uri) 68 clearCache() 69 } 70 } 71 ) 72 73 contentResolver.registerContentObserver( 74 Settings.Global.getUriFor(TAG_PATH), 75 true, 76 object : ContentObserver(Handler(mainLooper)) { 77 override fun onChange(selfChange: Boolean, uri: Uri?) { 78 super.onChange(selfChange, uri) 79 clearCache() 80 } 81 } 82 ) 83 Trace.endSection() 84 } 85 86 /** Whether [bufferName] should echo messages of [level] or higher to logcat. */ 87 @Synchronized isBufferLoggablenull88 override fun isBufferLoggable(bufferName: String, level: LogLevel): Boolean { 89 return level.ordinal >= getLogLevel(bufferName, BUFFER_PATH, cachedBufferLevels).ordinal 90 } 91 92 /** Whether [tagName] should echo messages of [level] or higher to logcat. */ 93 @Synchronized isTagLoggablenull94 override fun isTagLoggable(tagName: String, level: LogLevel): Boolean { 95 return level >= getLogLevel(tagName, TAG_PATH, cachedTagLevels) 96 } 97 getLogLevelnull98 private fun getLogLevel( 99 name: String, 100 path: String, 101 cache: MutableMap<String, LogLevel> 102 ): LogLevel { 103 return cache[name] ?: readSetting("$path/$name").also { cache[name] = it } 104 } 105 readSettingnull106 private fun readSetting(path: String): LogLevel { 107 return try { 108 Trace.beginSection("LogcatEchoTrackerDebug#readSetting") 109 parseProp(Settings.Global.getString(contentResolver, path)) 110 } catch (_: Settings.SettingNotFoundException) { 111 DEFAULT_LEVEL 112 } finally { 113 Trace.endSection() 114 } 115 } 116 parsePropnull117 private fun parseProp(propValue: String?): LogLevel { 118 return when (propValue?.lowercase()) { 119 "verbose" -> LogLevel.VERBOSE 120 "v" -> LogLevel.VERBOSE 121 "debug" -> LogLevel.DEBUG 122 "d" -> LogLevel.DEBUG 123 "info" -> LogLevel.INFO 124 "i" -> LogLevel.INFO 125 "warning" -> LogLevel.WARNING 126 "warn" -> LogLevel.WARNING 127 "w" -> LogLevel.WARNING 128 "error" -> LogLevel.ERROR 129 "e" -> LogLevel.ERROR 130 "assert" -> LogLevel.WTF 131 "wtf" -> LogLevel.WTF 132 else -> DEFAULT_LEVEL 133 } 134 } 135 } 136 137 private val DEFAULT_LEVEL = LogLevel.WARNING 138 private const val BUFFER_PATH = "systemui/buffer" 139 private const val TAG_PATH = "systemui/tag" 140