Loading packages/SystemUI/src/com/android/systemui/dump/DumpsysTableLogger.kt 0 → 100644 +125 −0 Original line number Diff line number Diff line /* * Copyright (C) 2022 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.systemui.dump import java.io.PrintWriter /** * Utility for logging nice table data to be parsed (and pretty printed) in bugreports. The general * idea here is to feed your nice, table-like data to this class, which embeds the schema and rows * into the dumpsys, wrapped in a known start and stop tags. Later, one can build a simple parser * and pretty-print this data in a table * * Note: Something should be said here about silently eating errors by filtering out malformed * lines. Because this class is expected to be utilized only during a dumpsys, it doesn't feel * most correct to throw an exception here (since an exception can often be the reason that this * class is created). Because of this, [DumpsysTableLogger] will simply filter out invalid lines * based solely on line length. This behavior might need to be revisited in the future. * * USAGE: * Assuming we have some data that would be logged to dumpsys like so: * * ``` * 1: field1=val1, field2=val2..., fieldN=valN * //... * M: field1M=val1M, ..., fieldNM * ``` * * You can break the `field<n>` values out into a columns spec: * ``` * val cols = [field1, field2,...,fieldN] * ``` * And then take all of the historical data lines (1 through M), and break them out into their own * lists: * ``` * val rows = [ * [field10, field20,..., fieldN0], * //... * [field1M, field2M,..., fieldNM] * ] * ``` * * Lastly, create a bugreport-unique section name, and use the table logger to write the data to * dumpsys: * ``` * val logger = DumpsysTableLogger(uniqueName, cols, rows) * logger.printTableData(pw) * ``` * * The expected output in the dumpsys would be: * ``` * SystemUI TableSection START: <SectionName> * version 1 * col1|col2|...|colN * field10|field20|...|fieldN0 * //... * field1M|field2M|...|fieldNM * SystemUI TableSection END: <SectionName> * ``` * * @param sectionName A name for the table data section. Should be unique in the bugreport * @param columns Definition for the columns of the table. This should be the same length as all * data rows * @param rows List of rows to be displayed in the table */ class DumpsysTableLogger( private val sectionName: String, private val columns: List<String>, private val rows: List<Row> ) { fun printTableData(pw: PrintWriter) { printSectionStart(pw) printSchema(pw) printData(pw) printSectionEnd(pw) } private fun printSectionStart(pw: PrintWriter) { pw.println(HEADER_PREFIX + sectionName) pw.println("version $VERSION") } private fun printSectionEnd(pw: PrintWriter) { pw.println(FOOTER_PREFIX + sectionName) } private fun printSchema(pw: PrintWriter) { pw.println(columns.joinToString(separator = SEPARATOR)) } private fun printData(pw: PrintWriter) { val count = columns.size rows .filter { it.size == count } .forEach { dataLine -> pw.println(dataLine.joinToString(separator = SEPARATOR)) } } } typealias Row = List<String> /** * DO NOT CHANGE! (but if you must...) * 1. Update the version number * 2. Update any consumers to parse the new version */ private const val HEADER_PREFIX = "SystemUI TableSection START: " private const val FOOTER_PREFIX = "SystemUI TableSection END: " private const val SEPARATOR = "|" // TBD private const val VERSION = "1" No newline at end of file packages/SystemUI/src/com/android/systemui/statusbar/connectivity/ConnectivityState.kt +28 −0 Original line number Diff line number Diff line Loading @@ -46,6 +46,34 @@ open class ConnectivityState { } } protected open fun tableColumns(): List<String> { return listOf( "connected", "enabled", "activityIn", "activityOut", "level", "iconGroup", "inetCondition", "rssi", "time") } protected open fun tableData(): List<String> { return listOf( connected, enabled, activityIn, activityOut, level, iconGroup, inetCondition, rssi, sSDF.format(time)).map { it.toString() } } protected open fun copyFrom(other: ConnectivityState) { connected = other.connected enabled = other.enabled Loading packages/SystemUI/src/com/android/systemui/statusbar/connectivity/MobileSignalController.java +2 −0 Original line number Diff line number Diff line Loading @@ -828,6 +828,8 @@ public class MobileSignalController extends SignalController<MobileState, Mobile + (mMobileStatusHistoryIndex + STATUS_HISTORY_SIZE - i) + "): " + mMobileStatusHistory[i & (STATUS_HISTORY_SIZE - 1)]); } dumpTableData(pw); } /** Box for QS icon info */ Loading packages/SystemUI/src/com/android/systemui/statusbar/connectivity/MobileState.kt +46 −0 Original line number Diff line number Diff line Loading @@ -162,6 +162,52 @@ internal class MobileState( builder.append("displayInfo=$telephonyDisplayInfo") } override fun tableColumns(): List<String> { val columns = listOf("dataSim", "networkName", "networkNameData", "dataConnected", "roaming", "isDefault", "isEmergency", "airplaneMode", "carrierNetworkChangeMode", "userSetup", "dataState", "defaultDataOff", "showQuickSettingsRatIcon", "voiceServiceState", "isInService", "serviceState", "signalStrength", "displayInfo") return super.tableColumns() + columns } override fun tableData(): List<String> { val columns = listOf(dataSim, networkName, networkNameData, dataConnected, roaming, isDefault, isEmergency, airplaneMode, carrierNetworkChangeMode, userSetup, dataState, defaultDataOff, showQuickSettingsRatIcon(), getVoiceServiceState(), isInService(), serviceState?.minLog() ?: "(null)", signalStrength?.minLog() ?: "(null)", telephonyDisplayInfo).map { it.toString() } return super.tableData() + columns } override fun equals(other: Any?): Boolean { if (this === other) return true if (javaClass != other?.javaClass) return false Loading packages/SystemUI/src/com/android/systemui/statusbar/connectivity/SignalController.java +61 −10 Original line number Diff line number Diff line Loading @@ -22,9 +22,12 @@ import android.content.Context; import android.util.Log; import com.android.settingslib.SignalIcon.IconGroup; import com.android.systemui.dump.DumpsysTableLogger; import java.io.PrintWriter; import java.util.ArrayList; import java.util.BitSet; import java.util.List; /** Loading Loading @@ -193,6 +196,26 @@ public abstract class SignalController<T extends ConnectivityState, I extends Ic pw.println(" - " + mTag + " -----"); pw.println(" Current State: " + mCurrentState); if (RECORD_HISTORY) { List<ConnectivityState> history = getOrderedHistoryExcludingCurrentState(); for (int i = 0; i < history.size(); i++) { pw.println(" Previous State(" + (i + 1) + "): " + mHistory[i]); } } } /** * mHistory is a ring, so use this method to get the time-ordered (from youngest to oldest) * list of historical states. Filters out any state whose `time` is `0`. * * For ease of compatibility, this list returns JUST the historical states, not the current * state which has yet to be copied into the history * * @see #getOrderedHistory() * @return historical states, ordered from newest to oldest */ List<ConnectivityState> getOrderedHistoryExcludingCurrentState() { ArrayList<ConnectivityState> history = new ArrayList<>(); // Count up the states that actually contain time stamps, and only display those. int size = 0; for (int i = 0; i < HISTORY_SIZE; i++) { Loading @@ -201,10 +224,38 @@ public abstract class SignalController<T extends ConnectivityState, I extends Ic // Print out the previous states in ordered number. for (int i = mHistoryIndex + HISTORY_SIZE - 1; i >= mHistoryIndex + HISTORY_SIZE - size; i--) { pw.println(" Previous State(" + (mHistoryIndex + HISTORY_SIZE - i) + "): " + mHistory[i & (HISTORY_SIZE - 1)]); history.add(mHistory[i & (HISTORY_SIZE - 1)]); } return history; } /** * Get the ordered history states, including the current yet-to-be-copied state. Useful for * logging * * @see #getOrderedHistoryExcludingCurrentState() * @return [currentState, historicalState...] array */ List<ConnectivityState> getOrderedHistory() { ArrayList<ConnectivityState> history = new ArrayList<>(); // Start with the current state history.add(mCurrentState); history.addAll(getOrderedHistoryExcludingCurrentState()); return history; } void dumpTableData(PrintWriter pw) { List<List<String>> tableData = new ArrayList<List<String>>(); List<ConnectivityState> history = getOrderedHistory(); for (int i = 0; i < history.size(); i++) { tableData.add(history.get(i).tableData()); } DumpsysTableLogger logger = new DumpsysTableLogger(mTag, mCurrentState.tableColumns(), tableData); logger.printTableData(pw); } final void notifyListeners() { Loading Loading
packages/SystemUI/src/com/android/systemui/dump/DumpsysTableLogger.kt 0 → 100644 +125 −0 Original line number Diff line number Diff line /* * Copyright (C) 2022 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.systemui.dump import java.io.PrintWriter /** * Utility for logging nice table data to be parsed (and pretty printed) in bugreports. The general * idea here is to feed your nice, table-like data to this class, which embeds the schema and rows * into the dumpsys, wrapped in a known start and stop tags. Later, one can build a simple parser * and pretty-print this data in a table * * Note: Something should be said here about silently eating errors by filtering out malformed * lines. Because this class is expected to be utilized only during a dumpsys, it doesn't feel * most correct to throw an exception here (since an exception can often be the reason that this * class is created). Because of this, [DumpsysTableLogger] will simply filter out invalid lines * based solely on line length. This behavior might need to be revisited in the future. * * USAGE: * Assuming we have some data that would be logged to dumpsys like so: * * ``` * 1: field1=val1, field2=val2..., fieldN=valN * //... * M: field1M=val1M, ..., fieldNM * ``` * * You can break the `field<n>` values out into a columns spec: * ``` * val cols = [field1, field2,...,fieldN] * ``` * And then take all of the historical data lines (1 through M), and break them out into their own * lists: * ``` * val rows = [ * [field10, field20,..., fieldN0], * //... * [field1M, field2M,..., fieldNM] * ] * ``` * * Lastly, create a bugreport-unique section name, and use the table logger to write the data to * dumpsys: * ``` * val logger = DumpsysTableLogger(uniqueName, cols, rows) * logger.printTableData(pw) * ``` * * The expected output in the dumpsys would be: * ``` * SystemUI TableSection START: <SectionName> * version 1 * col1|col2|...|colN * field10|field20|...|fieldN0 * //... * field1M|field2M|...|fieldNM * SystemUI TableSection END: <SectionName> * ``` * * @param sectionName A name for the table data section. Should be unique in the bugreport * @param columns Definition for the columns of the table. This should be the same length as all * data rows * @param rows List of rows to be displayed in the table */ class DumpsysTableLogger( private val sectionName: String, private val columns: List<String>, private val rows: List<Row> ) { fun printTableData(pw: PrintWriter) { printSectionStart(pw) printSchema(pw) printData(pw) printSectionEnd(pw) } private fun printSectionStart(pw: PrintWriter) { pw.println(HEADER_PREFIX + sectionName) pw.println("version $VERSION") } private fun printSectionEnd(pw: PrintWriter) { pw.println(FOOTER_PREFIX + sectionName) } private fun printSchema(pw: PrintWriter) { pw.println(columns.joinToString(separator = SEPARATOR)) } private fun printData(pw: PrintWriter) { val count = columns.size rows .filter { it.size == count } .forEach { dataLine -> pw.println(dataLine.joinToString(separator = SEPARATOR)) } } } typealias Row = List<String> /** * DO NOT CHANGE! (but if you must...) * 1. Update the version number * 2. Update any consumers to parse the new version */ private const val HEADER_PREFIX = "SystemUI TableSection START: " private const val FOOTER_PREFIX = "SystemUI TableSection END: " private const val SEPARATOR = "|" // TBD private const val VERSION = "1" No newline at end of file
packages/SystemUI/src/com/android/systemui/statusbar/connectivity/ConnectivityState.kt +28 −0 Original line number Diff line number Diff line Loading @@ -46,6 +46,34 @@ open class ConnectivityState { } } protected open fun tableColumns(): List<String> { return listOf( "connected", "enabled", "activityIn", "activityOut", "level", "iconGroup", "inetCondition", "rssi", "time") } protected open fun tableData(): List<String> { return listOf( connected, enabled, activityIn, activityOut, level, iconGroup, inetCondition, rssi, sSDF.format(time)).map { it.toString() } } protected open fun copyFrom(other: ConnectivityState) { connected = other.connected enabled = other.enabled Loading
packages/SystemUI/src/com/android/systemui/statusbar/connectivity/MobileSignalController.java +2 −0 Original line number Diff line number Diff line Loading @@ -828,6 +828,8 @@ public class MobileSignalController extends SignalController<MobileState, Mobile + (mMobileStatusHistoryIndex + STATUS_HISTORY_SIZE - i) + "): " + mMobileStatusHistory[i & (STATUS_HISTORY_SIZE - 1)]); } dumpTableData(pw); } /** Box for QS icon info */ Loading
packages/SystemUI/src/com/android/systemui/statusbar/connectivity/MobileState.kt +46 −0 Original line number Diff line number Diff line Loading @@ -162,6 +162,52 @@ internal class MobileState( builder.append("displayInfo=$telephonyDisplayInfo") } override fun tableColumns(): List<String> { val columns = listOf("dataSim", "networkName", "networkNameData", "dataConnected", "roaming", "isDefault", "isEmergency", "airplaneMode", "carrierNetworkChangeMode", "userSetup", "dataState", "defaultDataOff", "showQuickSettingsRatIcon", "voiceServiceState", "isInService", "serviceState", "signalStrength", "displayInfo") return super.tableColumns() + columns } override fun tableData(): List<String> { val columns = listOf(dataSim, networkName, networkNameData, dataConnected, roaming, isDefault, isEmergency, airplaneMode, carrierNetworkChangeMode, userSetup, dataState, defaultDataOff, showQuickSettingsRatIcon(), getVoiceServiceState(), isInService(), serviceState?.minLog() ?: "(null)", signalStrength?.minLog() ?: "(null)", telephonyDisplayInfo).map { it.toString() } return super.tableData() + columns } override fun equals(other: Any?): Boolean { if (this === other) return true if (javaClass != other?.javaClass) return false Loading
packages/SystemUI/src/com/android/systemui/statusbar/connectivity/SignalController.java +61 −10 Original line number Diff line number Diff line Loading @@ -22,9 +22,12 @@ import android.content.Context; import android.util.Log; import com.android.settingslib.SignalIcon.IconGroup; import com.android.systemui.dump.DumpsysTableLogger; import java.io.PrintWriter; import java.util.ArrayList; import java.util.BitSet; import java.util.List; /** Loading Loading @@ -193,6 +196,26 @@ public abstract class SignalController<T extends ConnectivityState, I extends Ic pw.println(" - " + mTag + " -----"); pw.println(" Current State: " + mCurrentState); if (RECORD_HISTORY) { List<ConnectivityState> history = getOrderedHistoryExcludingCurrentState(); for (int i = 0; i < history.size(); i++) { pw.println(" Previous State(" + (i + 1) + "): " + mHistory[i]); } } } /** * mHistory is a ring, so use this method to get the time-ordered (from youngest to oldest) * list of historical states. Filters out any state whose `time` is `0`. * * For ease of compatibility, this list returns JUST the historical states, not the current * state which has yet to be copied into the history * * @see #getOrderedHistory() * @return historical states, ordered from newest to oldest */ List<ConnectivityState> getOrderedHistoryExcludingCurrentState() { ArrayList<ConnectivityState> history = new ArrayList<>(); // Count up the states that actually contain time stamps, and only display those. int size = 0; for (int i = 0; i < HISTORY_SIZE; i++) { Loading @@ -201,10 +224,38 @@ public abstract class SignalController<T extends ConnectivityState, I extends Ic // Print out the previous states in ordered number. for (int i = mHistoryIndex + HISTORY_SIZE - 1; i >= mHistoryIndex + HISTORY_SIZE - size; i--) { pw.println(" Previous State(" + (mHistoryIndex + HISTORY_SIZE - i) + "): " + mHistory[i & (HISTORY_SIZE - 1)]); history.add(mHistory[i & (HISTORY_SIZE - 1)]); } return history; } /** * Get the ordered history states, including the current yet-to-be-copied state. Useful for * logging * * @see #getOrderedHistoryExcludingCurrentState() * @return [currentState, historicalState...] array */ List<ConnectivityState> getOrderedHistory() { ArrayList<ConnectivityState> history = new ArrayList<>(); // Start with the current state history.add(mCurrentState); history.addAll(getOrderedHistoryExcludingCurrentState()); return history; } void dumpTableData(PrintWriter pw) { List<List<String>> tableData = new ArrayList<List<String>>(); List<ConnectivityState> history = getOrderedHistory(); for (int i = 0; i < history.size(); i++) { tableData.add(history.get(i).tableData()); } DumpsysTableLogger logger = new DumpsysTableLogger(mTag, mCurrentState.tableColumns(), tableData); logger.printTableData(pw); } final void notifyListeners() { Loading