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.qs.tiles.base.logging 18 19 import androidx.annotation.GuardedBy 20 import com.android.systemui.dagger.SysUISingleton 21 import com.android.systemui.log.LogBuffer 22 import com.android.systemui.log.LogBufferFactory 23 import com.android.systemui.log.core.LogLevel 24 import com.android.systemui.log.dagger.QSTilesLogBuffers 25 import com.android.systemui.plugins.statusbar.StatusBarStateController 26 import com.android.systemui.qs.pipeline.shared.TileSpec 27 import com.android.systemui.qs.tiles.viewmodel.QSTileState 28 import com.android.systemui.qs.tiles.viewmodel.QSTileUserAction 29 import com.android.systemui.statusbar.StatusBarState 30 import javax.inject.Inject 31 32 @SysUISingleton 33 class QSTileLogger 34 @Inject 35 constructor( 36 @QSTilesLogBuffers logBuffers: Map<TileSpec, LogBuffer>, 37 private val factory: LogBufferFactory, 38 private val mStatusBarStateController: StatusBarStateController, 39 ) { 40 @GuardedBy("logBufferCache") private val logBufferCache = logBuffers.toMutableMap() 41 42 /** 43 * Tracks user action when it's first received by the ViewModel and before it reaches the 44 * pipeline 45 */ logUserActionnull46 fun logUserAction( 47 userAction: QSTileUserAction, 48 tileSpec: TileSpec, 49 hasData: Boolean, 50 hasTileState: Boolean, 51 ) { 52 tileSpec 53 .getLogBuffer() 54 .log( 55 tileSpec.getLogTag(), 56 LogLevel.DEBUG, 57 { 58 str1 = userAction.toLogString() 59 int1 = mStatusBarStateController.state 60 bool1 = hasTileState 61 bool2 = hasData 62 }, 63 { 64 "tile $str1: " + 65 "statusBarState=${StatusBarState.toString(int1)}, " + 66 "hasState=$bool1, " + 67 "hasData=$bool2" 68 } 69 ) 70 } 71 72 /** Tracks user action when it's rejected by false gestures */ logUserActionRejectedByFalsingnull73 fun logUserActionRejectedByFalsing( 74 userAction: QSTileUserAction, 75 tileSpec: TileSpec, 76 ) { 77 tileSpec 78 .getLogBuffer() 79 .log( 80 tileSpec.getLogTag(), 81 LogLevel.DEBUG, 82 { str1 = userAction.toLogString() }, 83 { "tile $str1: rejected by falsing" } 84 ) 85 } 86 87 /** Tracks user action when it's rejected according to the policy */ logUserActionRejectedByPolicynull88 fun logUserActionRejectedByPolicy( 89 userAction: QSTileUserAction, 90 tileSpec: TileSpec, 91 restriction: String, 92 ) { 93 tileSpec 94 .getLogBuffer() 95 .log( 96 tileSpec.getLogTag(), 97 LogLevel.DEBUG, 98 { str1 = userAction.toLogString() }, 99 { "tile $str1: rejected by policy, restriction: $restriction" } 100 ) 101 } 102 103 /** 104 * Tracks user actions when it reaches the pipeline and mixes with the last tile state and data 105 */ logUserActionPipelinenull106 fun <T> logUserActionPipeline( 107 tileSpec: TileSpec, 108 userAction: QSTileUserAction, 109 tileState: QSTileState, 110 data: T, 111 ) { 112 tileSpec 113 .getLogBuffer() 114 .log( 115 tileSpec.getLogTag(), 116 LogLevel.DEBUG, 117 { 118 str1 = userAction.toLogString() 119 str2 = tileState.toLogString() 120 str3 = data.toString().take(DATA_MAX_LENGTH) 121 }, 122 { 123 "tile $str1 pipeline: " + 124 "statusBarState=${StatusBarState.toString(int1)}, " + 125 "state=$str2, " + 126 "data=$str3" 127 } 128 ) 129 } 130 logForceUpdatenull131 fun logForceUpdate(tileSpec: TileSpec) { 132 tileSpec 133 .getLogBuffer() 134 .log(tileSpec.getLogTag(), LogLevel.DEBUG, {}, { "tile data force update" }) 135 } 136 logInitialRequestnull137 fun logInitialRequest(tileSpec: TileSpec) { 138 tileSpec 139 .getLogBuffer() 140 .log(tileSpec.getLogTag(), LogLevel.DEBUG, {}, { "tile data initial update" }) 141 } 142 143 /** Tracks state changes based on the data and trigger event. */ logStateUpdatenull144 fun <T> logStateUpdate( 145 tileSpec: TileSpec, 146 tileState: QSTileState, 147 data: T, 148 ) { 149 tileSpec 150 .getLogBuffer() 151 .log( 152 tileSpec.getLogTag(), 153 LogLevel.DEBUG, 154 { 155 str1 = tileState.toLogString() 156 str2 = data.toString().take(DATA_MAX_LENGTH) 157 }, 158 { "tile state update: state=$str1, data=$str2" } 159 ) 160 } 161 logErrornull162 fun logError( 163 tileSpec: TileSpec, 164 message: String, 165 error: Throwable, 166 ) { 167 tileSpec 168 .getLogBuffer() 169 .log( 170 tileSpec.getLogTag(), 171 LogLevel.ERROR, 172 {}, 173 { message }, 174 error, 175 ) 176 } 177 178 /** Log with level [LogLevel.WARNING] */ logWarningnull179 fun logWarning( 180 tileSpec: TileSpec, 181 message: String, 182 ) { 183 tileSpec 184 .getLogBuffer() 185 .log(tileSpec.getLogTag(), LogLevel.WARNING, { str1 = message }, { str1!! }) 186 } 187 188 /** Log with level [LogLevel.INFO] */ logInfonull189 fun logInfo( 190 tileSpec: TileSpec, 191 message: String, 192 ) { 193 tileSpec 194 .getLogBuffer() 195 .log(tileSpec.getLogTag(), LogLevel.INFO, { str1 = message }, { str1!! }) 196 } 197 logCustomTileUserActionDeliverednull198 fun logCustomTileUserActionDelivered(tileSpec: TileSpec) { 199 tileSpec 200 .getLogBuffer() 201 .log( 202 tileSpec.getLogTag(), 203 LogLevel.DEBUG, 204 {}, 205 { "user action delivered to the service" }, 206 ) 207 } 208 getLogTagnull209 private fun TileSpec.getLogTag(): String = "${TAG_FORMAT_PREFIX}_${this.spec}" 210 211 private fun TileSpec.getLogBuffer(): LogBuffer = 212 synchronized(logBufferCache) { 213 logBufferCache.getOrPut(this) { 214 factory.create( 215 this.getLogTag(), 216 BUFFER_MAX_SIZE /* maxSize */, 217 false /* systrace */ 218 ) 219 } 220 } 221 toLogStringnull222 private fun QSTileUserAction.toLogString(): String = 223 when (this) { 224 is QSTileUserAction.Click -> "click" 225 is QSTileUserAction.LongClick -> "long click" 226 } 227 228 /* Shortened version of a data class toString() */ toLogStringnull229 private fun QSTileState.toLogString(): String = 230 "[label=$label, " + 231 "state=$activationState, " + 232 "s_label=$secondaryLabel, " + 233 "cd=$contentDescription, " + 234 "sd=$stateDescription, " + 235 "svi=$sideViewIcon, " + 236 "enabled=$enabledState, " + 237 "a11y=$expandedAccessibilityClassName" + 238 "]" 239 240 private companion object { 241 const val TAG_FORMAT_PREFIX = "QSLog_tile_" 242 const val DATA_MAX_LENGTH = 50 243 const val BUFFER_MAX_SIZE = 25 244 } 245 } 246