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.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.provider.Settings 25 26 /** 27 * Version of [LogcatEchoTracker] for debuggable builds 28 * 29 * The log level of individual buffers or tags can be controlled via global settings: 30 * 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( 40 private val contentResolver: ContentResolver 41 ) : LogcatEchoTracker { 42 private val cachedBufferLevels: MutableMap<String, LogLevel> = mutableMapOf() 43 private val cachedTagLevels: MutableMap<String, LogLevel> = mutableMapOf() 44 45 companion object Factory { 46 @JvmStatic 47 fun create( 48 contentResolver: ContentResolver, 49 mainLooper: Looper 50 ): LogcatEchoTrackerDebug { 51 val tracker = LogcatEchoTrackerDebug(contentResolver) 52 tracker.attach(mainLooper) 53 return tracker 54 } 55 } 56 57 private fun attach(mainLooper: Looper) { 58 contentResolver.registerContentObserver( 59 Settings.Global.getUriFor(BUFFER_PATH), 60 true, 61 object : ContentObserver(Handler(mainLooper)) { 62 override fun onChange(selfChange: Boolean, uri: Uri) { 63 super.onChange(selfChange, uri) 64 cachedBufferLevels.clear() 65 } 66 }) 67 68 contentResolver.registerContentObserver( 69 Settings.Global.getUriFor(TAG_PATH), 70 true, 71 object : ContentObserver(Handler(mainLooper)) { 72 override fun onChange(selfChange: Boolean, uri: Uri) { 73 super.onChange(selfChange, uri) 74 cachedTagLevels.clear() 75 } 76 }) 77 } 78 79 /** 80 * Whether [bufferName] should echo messages of [level] or higher to logcat. 81 */ 82 @Synchronized 83 override fun isBufferLoggable(bufferName: String, level: LogLevel): Boolean { 84 return level.ordinal >= getLogLevel(bufferName, BUFFER_PATH, cachedBufferLevels).ordinal 85 } 86 87 /** 88 * Whether [tagName] should echo messages of [level] or higher to logcat. 89 */ 90 @Synchronized 91 override fun isTagLoggable(tagName: String, level: LogLevel): Boolean { 92 return level >= getLogLevel(tagName, TAG_PATH, cachedTagLevels) 93 } 94 95 private fun getLogLevel( 96 name: String, 97 path: String, 98 cache: MutableMap<String, LogLevel> 99 ): LogLevel { 100 return cache[name] ?: readSetting("$path/$name").also { cache[name] = it } 101 } 102 103 private fun readSetting(path: String): LogLevel { 104 return try { 105 parseProp(Settings.Global.getString(contentResolver, path)) 106 } catch (_: Settings.SettingNotFoundException) { 107 DEFAULT_LEVEL 108 } 109 } 110 111 private fun parseProp(propValue: String?): LogLevel { 112 return when (propValue?.toLowerCase()) { 113 "verbose" -> LogLevel.VERBOSE 114 "v" -> LogLevel.VERBOSE 115 "debug" -> LogLevel.DEBUG 116 "d" -> LogLevel.DEBUG 117 "info" -> LogLevel.INFO 118 "i" -> LogLevel.INFO 119 "warning" -> LogLevel.WARNING 120 "warn" -> LogLevel.WARNING 121 "w" -> LogLevel.WARNING 122 "error" -> LogLevel.ERROR 123 "e" -> LogLevel.ERROR 124 "assert" -> LogLevel.WTF 125 "wtf" -> LogLevel.WTF 126 else -> DEFAULT_LEVEL 127 } 128 } 129 } 130 131 private val DEFAULT_LEVEL = LogLevel.WARNING 132 private const val BUFFER_PATH = "systemui/buffer" 133 private const val TAG_PATH = "systemui/tag" 134