Loading packages/SystemUI/src/com/android/systemui/log/table/Diffable.kt +19 −3 Original line number Original line Diff line number Diff line Loading @@ -30,10 +30,11 @@ import kotlinx.coroutines.flow.Flow */ */ interface Diffable<T> { interface Diffable<T> { /** /** * Finds the differences between [prevVal] and [this] and logs those diffs to [row]. * Finds the differences between [prevVal] and this object and logs those diffs to [row]. * * * Each implementer should determine which individual fields have changed between [prevVal] and * Each implementer should determine which individual fields have changed between [prevVal] and * [this], and only log the fields that have actually changed. This helps save buffer space. * this object, and only log the fields that have actually changed. This helps save buffer * space. * * * For example, if: * For example, if: * - prevVal = Object(val1=100, val2=200, val3=300) * - prevVal = Object(val1=100, val2=200, val3=300) Loading @@ -42,6 +43,16 @@ interface Diffable<T> { * Then only the val3 change should be logged. * Then only the val3 change should be logged. */ */ fun logDiffs(prevVal: T, row: TableRowLogger) fun logDiffs(prevVal: T, row: TableRowLogger) /** * Logs all the relevant fields of this object to [row]. * * As opposed to [logDiffs], this method should log *all* fields. * * Implementation is optional. This method will only be used with [logDiffsForTable] in order to * fully log the initial value of the flow. */ fun logFull(row: TableRowLogger) {} } } /** /** Loading @@ -57,7 +68,12 @@ fun <T : Diffable<T>> Flow<T>.logDiffsForTable( columnPrefix: String, columnPrefix: String, initialValue: T, initialValue: T, ): Flow<T> { ): Flow<T> { return this.pairwiseBy(initialValue) { prevVal, newVal -> // Fully log the initial value to the table. val getInitialValue = { tableLogBuffer.logChange(columnPrefix) { row -> initialValue.logFull(row) } initialValue } return this.pairwiseBy(getInitialValue) { prevVal: T, newVal: T -> tableLogBuffer.logDiffs(columnPrefix, prevVal, newVal) tableLogBuffer.logDiffs(columnPrefix, prevVal, newVal) newVal newVal } } Loading packages/SystemUI/src/com/android/systemui/log/table/TableLogBuffer.kt +14 −0 Original line number Original line Diff line number Diff line Loading @@ -113,6 +113,20 @@ class TableLogBuffer( newVal.logDiffs(prevVal, row) newVal.logDiffs(prevVal, row) } } /** * Logs change(s) to the buffer using [rowInitializer]. * * @param rowInitializer a function that will be called immediately to store relevant data on * the row. */ @Synchronized fun logChange(columnPrefix: String, rowInitializer: (TableRowLogger) -> Unit) { val row = tempRow row.timestamp = systemClock.currentTimeMillis() row.columnPrefix = columnPrefix rowInitializer(row) } // Keep these individual [logChange] methods private (don't let clients give us their own // Keep these individual [logChange] methods private (don't let clients give us their own // timestamps.) // timestamps.) Loading packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/data/model/WifiNetworkModel.kt +13 −16 Original line number Original line Diff line number Diff line Loading @@ -31,15 +31,19 @@ sealed class WifiNetworkModel : Diffable<WifiNetworkModel> { if (prevVal is Inactive) { if (prevVal is Inactive) { return return } } row.logChange(COL_NETWORK_TYPE, TYPE_INACTIVE) if (prevVal is CarrierMerged) { if (prevVal is CarrierMerged) { // The only difference between CarrierMerged and Inactive is the type // The only difference between CarrierMerged and Inactive is the type row.logChange(COL_NETWORK_TYPE, TYPE_INACTIVE) return return } } // When changing from Active to Inactive, we need to log diffs to all the fields. // When changing from Active to Inactive, we need to log diffs to all the fields. logDiffsFromActiveToNotActive(prevVal as Active, row) logFullNonActiveNetwork(TYPE_INACTIVE, row) } override fun logFull(row: TableRowLogger) { logFullNonActiveNetwork(TYPE_INACTIVE, row) } } } } Loading @@ -56,15 +60,15 @@ sealed class WifiNetworkModel : Diffable<WifiNetworkModel> { if (prevVal is CarrierMerged) { if (prevVal is CarrierMerged) { return return } } row.logChange(COL_NETWORK_TYPE, TYPE_CARRIER_MERGED) if (prevVal is Inactive) { if (prevVal is Inactive) { // The only difference between CarrierMerged and Inactive is the type. // The only difference between CarrierMerged and Inactive is the type. row.logChange(COL_NETWORK_TYPE, TYPE_CARRIER_MERGED) return return } } // When changing from Active to CarrierMerged, we need to log diffs to all the fields. // When changing from Active to CarrierMerged, we need to log diffs to all the fields. logDiffsFromActiveToNotActive(prevVal as Active, row) logFullNonActiveNetwork(TYPE_CARRIER_MERGED, row) } } } } Loading Loading @@ -147,7 +151,6 @@ sealed class WifiNetworkModel : Diffable<WifiNetworkModel> { } } } } override fun toString(): String { override fun toString(): String { // Only include the passpoint-related values in the string if we have them. (Most // Only include the passpoint-related values in the string if we have them. (Most // networks won't have them so they'll be mostly clutter.) // networks won't have them so they'll be mostly clutter.) Loading @@ -174,23 +177,17 @@ sealed class WifiNetworkModel : Diffable<WifiNetworkModel> { } } } } internal fun logDiffsFromActiveToNotActive(prevActive: Active, row: TableRowLogger) { internal fun logFullNonActiveNetwork(type: String, row: TableRowLogger) { row.logChange(COL_NETWORK_TYPE, type) row.logChange(COL_NETWORK_ID, NETWORK_ID_DEFAULT) row.logChange(COL_NETWORK_ID, NETWORK_ID_DEFAULT) row.logChange(COL_VALIDATED, false) row.logChange(COL_VALIDATED, false) row.logChange(COL_LEVEL, LEVEL_DEFAULT) row.logChange(COL_LEVEL, LEVEL_DEFAULT) row.logChange(COL_SSID, null) row.logChange(COL_SSID, null) if (prevActive.isPasspointAccessPoint) { row.logChange(COL_PASSPOINT_ACCESS_POINT, false) row.logChange(COL_PASSPOINT_ACCESS_POINT, false) } if (prevActive.isOnlineSignUpForPasspointAccessPoint) { row.logChange(COL_ONLINE_SIGN_UP, false) row.logChange(COL_ONLINE_SIGN_UP, false) } if (prevActive.passpointProviderFriendlyName != null) { row.logChange(COL_PASSPOINT_NAME, null) row.logChange(COL_PASSPOINT_NAME, null) } } } } } const val TYPE_CARRIER_MERGED = "CarrierMerged" const val TYPE_CARRIER_MERGED = "CarrierMerged" const val TYPE_INACTIVE = "Inactive" const val TYPE_INACTIVE = "Inactive" Loading packages/SystemUI/src/com/android/systemui/util/kotlin/Flow.kt +16 −1 Original line number Original line Diff line number Diff line Loading @@ -20,7 +20,6 @@ import java.util.concurrent.atomic.AtomicReference import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.coroutineScope import kotlinx.coroutines.coroutineScope import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.collect import kotlinx.coroutines.flow.distinctUntilChanged import kotlinx.coroutines.flow.distinctUntilChanged import kotlinx.coroutines.flow.flow import kotlinx.coroutines.flow.flow import kotlinx.coroutines.flow.onStart import kotlinx.coroutines.flow.onStart Loading Loading @@ -57,6 +56,22 @@ fun <T, R> Flow<T>.pairwiseBy( ): Flow<R> = ): Flow<R> = onStart { emit(initialValue) }.pairwiseBy(transform) onStart { emit(initialValue) }.pairwiseBy(transform) /** * Returns a new [Flow] that combines the two most recent emissions from [this] using [transform]. * * * The output of [getInitialValue] will be used as the "old" value for the first emission. As * opposed to the initial value in the above [pairwiseBy], [getInitialValue] can do some work before * returning the initial value. * * Useful for code that needs to compare the current value to the previous value. */ fun <T, R> Flow<T>.pairwiseBy( getInitialValue: suspend () -> T, transform: suspend (previousValue: T, newValue: T) -> R, ): Flow<R> = onStart { emit(getInitialValue()) }.pairwiseBy(transform) /** /** * Returns a new [Flow] that produces the two most recent emissions from [this]. Note that the new * Returns a new [Flow] that produces the two most recent emissions from [this]. Note that the new * Flow will not start emitting until it has received two emissions from the upstream Flow. * Flow will not start emitting until it has received two emissions from the upstream Flow. Loading packages/SystemUI/tests/src/com/android/systemui/log/table/TableLogBufferTest.kt +62 −0 Original line number Original line Diff line number Diff line Loading @@ -336,6 +336,68 @@ class TableLogBufferTest : SysuiTestCase() { assertThat(dumpedString).contains(expected2) assertThat(dumpedString).contains(expected2) } } @Test fun logChange_rowInitializer_dumpsCorrectly() { systemClock.setCurrentTimeMillis(100L) underTest.logChange("") { row -> row.logChange("column1", "val1") row.logChange("column2", 2) row.logChange("column3", true) } val dumpedString = dumpChanges() val timestamp = TABLE_LOG_DATE_FORMAT.format(100L) val expected1 = timestamp + SEPARATOR + "column1" + SEPARATOR + "val1" val expected2 = timestamp + SEPARATOR + "column2" + SEPARATOR + "2" val expected3 = timestamp + SEPARATOR + "column3" + SEPARATOR + "true" assertThat(dumpedString).contains(expected1) assertThat(dumpedString).contains(expected2) assertThat(dumpedString).contains(expected3) } @Test fun logChangeAndLogDiffs_bothLogged() { systemClock.setCurrentTimeMillis(100L) underTest.logChange("") { row -> row.logChange("column1", "val1") row.logChange("column2", 2) row.logChange("column3", true) } systemClock.setCurrentTimeMillis(200L) val prevDiffable = object : TestDiffable() {} val nextDiffable = object : TestDiffable() { override fun logDiffs(prevVal: TestDiffable, row: TableRowLogger) { row.logChange("column1", "newVal1") row.logChange("column2", 222) row.logChange("column3", false) } } underTest.logDiffs(columnPrefix = "", prevDiffable, nextDiffable) val dumpedString = dumpChanges() val timestamp1 = TABLE_LOG_DATE_FORMAT.format(100L) val expected1 = timestamp1 + SEPARATOR + "column1" + SEPARATOR + "val1" val expected2 = timestamp1 + SEPARATOR + "column2" + SEPARATOR + "2" val expected3 = timestamp1 + SEPARATOR + "column3" + SEPARATOR + "true" val timestamp2 = TABLE_LOG_DATE_FORMAT.format(200L) val expected4 = timestamp2 + SEPARATOR + "column1" + SEPARATOR + "newVal1" val expected5 = timestamp2 + SEPARATOR + "column2" + SEPARATOR + "222" val expected6 = timestamp2 + SEPARATOR + "column3" + SEPARATOR + "false" assertThat(dumpedString).contains(expected1) assertThat(dumpedString).contains(expected2) assertThat(dumpedString).contains(expected3) assertThat(dumpedString).contains(expected4) assertThat(dumpedString).contains(expected5) assertThat(dumpedString).contains(expected6) } @Test @Test fun dumpChanges_rotatesIfBufferIsFull() { fun dumpChanges_rotatesIfBufferIsFull() { lateinit var valToDump: String lateinit var valToDump: String Loading Loading
packages/SystemUI/src/com/android/systemui/log/table/Diffable.kt +19 −3 Original line number Original line Diff line number Diff line Loading @@ -30,10 +30,11 @@ import kotlinx.coroutines.flow.Flow */ */ interface Diffable<T> { interface Diffable<T> { /** /** * Finds the differences between [prevVal] and [this] and logs those diffs to [row]. * Finds the differences between [prevVal] and this object and logs those diffs to [row]. * * * Each implementer should determine which individual fields have changed between [prevVal] and * Each implementer should determine which individual fields have changed between [prevVal] and * [this], and only log the fields that have actually changed. This helps save buffer space. * this object, and only log the fields that have actually changed. This helps save buffer * space. * * * For example, if: * For example, if: * - prevVal = Object(val1=100, val2=200, val3=300) * - prevVal = Object(val1=100, val2=200, val3=300) Loading @@ -42,6 +43,16 @@ interface Diffable<T> { * Then only the val3 change should be logged. * Then only the val3 change should be logged. */ */ fun logDiffs(prevVal: T, row: TableRowLogger) fun logDiffs(prevVal: T, row: TableRowLogger) /** * Logs all the relevant fields of this object to [row]. * * As opposed to [logDiffs], this method should log *all* fields. * * Implementation is optional. This method will only be used with [logDiffsForTable] in order to * fully log the initial value of the flow. */ fun logFull(row: TableRowLogger) {} } } /** /** Loading @@ -57,7 +68,12 @@ fun <T : Diffable<T>> Flow<T>.logDiffsForTable( columnPrefix: String, columnPrefix: String, initialValue: T, initialValue: T, ): Flow<T> { ): Flow<T> { return this.pairwiseBy(initialValue) { prevVal, newVal -> // Fully log the initial value to the table. val getInitialValue = { tableLogBuffer.logChange(columnPrefix) { row -> initialValue.logFull(row) } initialValue } return this.pairwiseBy(getInitialValue) { prevVal: T, newVal: T -> tableLogBuffer.logDiffs(columnPrefix, prevVal, newVal) tableLogBuffer.logDiffs(columnPrefix, prevVal, newVal) newVal newVal } } Loading
packages/SystemUI/src/com/android/systemui/log/table/TableLogBuffer.kt +14 −0 Original line number Original line Diff line number Diff line Loading @@ -113,6 +113,20 @@ class TableLogBuffer( newVal.logDiffs(prevVal, row) newVal.logDiffs(prevVal, row) } } /** * Logs change(s) to the buffer using [rowInitializer]. * * @param rowInitializer a function that will be called immediately to store relevant data on * the row. */ @Synchronized fun logChange(columnPrefix: String, rowInitializer: (TableRowLogger) -> Unit) { val row = tempRow row.timestamp = systemClock.currentTimeMillis() row.columnPrefix = columnPrefix rowInitializer(row) } // Keep these individual [logChange] methods private (don't let clients give us their own // Keep these individual [logChange] methods private (don't let clients give us their own // timestamps.) // timestamps.) Loading
packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/data/model/WifiNetworkModel.kt +13 −16 Original line number Original line Diff line number Diff line Loading @@ -31,15 +31,19 @@ sealed class WifiNetworkModel : Diffable<WifiNetworkModel> { if (prevVal is Inactive) { if (prevVal is Inactive) { return return } } row.logChange(COL_NETWORK_TYPE, TYPE_INACTIVE) if (prevVal is CarrierMerged) { if (prevVal is CarrierMerged) { // The only difference between CarrierMerged and Inactive is the type // The only difference between CarrierMerged and Inactive is the type row.logChange(COL_NETWORK_TYPE, TYPE_INACTIVE) return return } } // When changing from Active to Inactive, we need to log diffs to all the fields. // When changing from Active to Inactive, we need to log diffs to all the fields. logDiffsFromActiveToNotActive(prevVal as Active, row) logFullNonActiveNetwork(TYPE_INACTIVE, row) } override fun logFull(row: TableRowLogger) { logFullNonActiveNetwork(TYPE_INACTIVE, row) } } } } Loading @@ -56,15 +60,15 @@ sealed class WifiNetworkModel : Diffable<WifiNetworkModel> { if (prevVal is CarrierMerged) { if (prevVal is CarrierMerged) { return return } } row.logChange(COL_NETWORK_TYPE, TYPE_CARRIER_MERGED) if (prevVal is Inactive) { if (prevVal is Inactive) { // The only difference between CarrierMerged and Inactive is the type. // The only difference between CarrierMerged and Inactive is the type. row.logChange(COL_NETWORK_TYPE, TYPE_CARRIER_MERGED) return return } } // When changing from Active to CarrierMerged, we need to log diffs to all the fields. // When changing from Active to CarrierMerged, we need to log diffs to all the fields. logDiffsFromActiveToNotActive(prevVal as Active, row) logFullNonActiveNetwork(TYPE_CARRIER_MERGED, row) } } } } Loading Loading @@ -147,7 +151,6 @@ sealed class WifiNetworkModel : Diffable<WifiNetworkModel> { } } } } override fun toString(): String { override fun toString(): String { // Only include the passpoint-related values in the string if we have them. (Most // Only include the passpoint-related values in the string if we have them. (Most // networks won't have them so they'll be mostly clutter.) // networks won't have them so they'll be mostly clutter.) Loading @@ -174,23 +177,17 @@ sealed class WifiNetworkModel : Diffable<WifiNetworkModel> { } } } } internal fun logDiffsFromActiveToNotActive(prevActive: Active, row: TableRowLogger) { internal fun logFullNonActiveNetwork(type: String, row: TableRowLogger) { row.logChange(COL_NETWORK_TYPE, type) row.logChange(COL_NETWORK_ID, NETWORK_ID_DEFAULT) row.logChange(COL_NETWORK_ID, NETWORK_ID_DEFAULT) row.logChange(COL_VALIDATED, false) row.logChange(COL_VALIDATED, false) row.logChange(COL_LEVEL, LEVEL_DEFAULT) row.logChange(COL_LEVEL, LEVEL_DEFAULT) row.logChange(COL_SSID, null) row.logChange(COL_SSID, null) if (prevActive.isPasspointAccessPoint) { row.logChange(COL_PASSPOINT_ACCESS_POINT, false) row.logChange(COL_PASSPOINT_ACCESS_POINT, false) } if (prevActive.isOnlineSignUpForPasspointAccessPoint) { row.logChange(COL_ONLINE_SIGN_UP, false) row.logChange(COL_ONLINE_SIGN_UP, false) } if (prevActive.passpointProviderFriendlyName != null) { row.logChange(COL_PASSPOINT_NAME, null) row.logChange(COL_PASSPOINT_NAME, null) } } } } } const val TYPE_CARRIER_MERGED = "CarrierMerged" const val TYPE_CARRIER_MERGED = "CarrierMerged" const val TYPE_INACTIVE = "Inactive" const val TYPE_INACTIVE = "Inactive" Loading
packages/SystemUI/src/com/android/systemui/util/kotlin/Flow.kt +16 −1 Original line number Original line Diff line number Diff line Loading @@ -20,7 +20,6 @@ import java.util.concurrent.atomic.AtomicReference import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.coroutineScope import kotlinx.coroutines.coroutineScope import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.collect import kotlinx.coroutines.flow.distinctUntilChanged import kotlinx.coroutines.flow.distinctUntilChanged import kotlinx.coroutines.flow.flow import kotlinx.coroutines.flow.flow import kotlinx.coroutines.flow.onStart import kotlinx.coroutines.flow.onStart Loading Loading @@ -57,6 +56,22 @@ fun <T, R> Flow<T>.pairwiseBy( ): Flow<R> = ): Flow<R> = onStart { emit(initialValue) }.pairwiseBy(transform) onStart { emit(initialValue) }.pairwiseBy(transform) /** * Returns a new [Flow] that combines the two most recent emissions from [this] using [transform]. * * * The output of [getInitialValue] will be used as the "old" value for the first emission. As * opposed to the initial value in the above [pairwiseBy], [getInitialValue] can do some work before * returning the initial value. * * Useful for code that needs to compare the current value to the previous value. */ fun <T, R> Flow<T>.pairwiseBy( getInitialValue: suspend () -> T, transform: suspend (previousValue: T, newValue: T) -> R, ): Flow<R> = onStart { emit(getInitialValue()) }.pairwiseBy(transform) /** /** * Returns a new [Flow] that produces the two most recent emissions from [this]. Note that the new * Returns a new [Flow] that produces the two most recent emissions from [this]. Note that the new * Flow will not start emitting until it has received two emissions from the upstream Flow. * Flow will not start emitting until it has received two emissions from the upstream Flow. Loading
packages/SystemUI/tests/src/com/android/systemui/log/table/TableLogBufferTest.kt +62 −0 Original line number Original line Diff line number Diff line Loading @@ -336,6 +336,68 @@ class TableLogBufferTest : SysuiTestCase() { assertThat(dumpedString).contains(expected2) assertThat(dumpedString).contains(expected2) } } @Test fun logChange_rowInitializer_dumpsCorrectly() { systemClock.setCurrentTimeMillis(100L) underTest.logChange("") { row -> row.logChange("column1", "val1") row.logChange("column2", 2) row.logChange("column3", true) } val dumpedString = dumpChanges() val timestamp = TABLE_LOG_DATE_FORMAT.format(100L) val expected1 = timestamp + SEPARATOR + "column1" + SEPARATOR + "val1" val expected2 = timestamp + SEPARATOR + "column2" + SEPARATOR + "2" val expected3 = timestamp + SEPARATOR + "column3" + SEPARATOR + "true" assertThat(dumpedString).contains(expected1) assertThat(dumpedString).contains(expected2) assertThat(dumpedString).contains(expected3) } @Test fun logChangeAndLogDiffs_bothLogged() { systemClock.setCurrentTimeMillis(100L) underTest.logChange("") { row -> row.logChange("column1", "val1") row.logChange("column2", 2) row.logChange("column3", true) } systemClock.setCurrentTimeMillis(200L) val prevDiffable = object : TestDiffable() {} val nextDiffable = object : TestDiffable() { override fun logDiffs(prevVal: TestDiffable, row: TableRowLogger) { row.logChange("column1", "newVal1") row.logChange("column2", 222) row.logChange("column3", false) } } underTest.logDiffs(columnPrefix = "", prevDiffable, nextDiffable) val dumpedString = dumpChanges() val timestamp1 = TABLE_LOG_DATE_FORMAT.format(100L) val expected1 = timestamp1 + SEPARATOR + "column1" + SEPARATOR + "val1" val expected2 = timestamp1 + SEPARATOR + "column2" + SEPARATOR + "2" val expected3 = timestamp1 + SEPARATOR + "column3" + SEPARATOR + "true" val timestamp2 = TABLE_LOG_DATE_FORMAT.format(200L) val expected4 = timestamp2 + SEPARATOR + "column1" + SEPARATOR + "newVal1" val expected5 = timestamp2 + SEPARATOR + "column2" + SEPARATOR + "222" val expected6 = timestamp2 + SEPARATOR + "column3" + SEPARATOR + "false" assertThat(dumpedString).contains(expected1) assertThat(dumpedString).contains(expected2) assertThat(dumpedString).contains(expected3) assertThat(dumpedString).contains(expected4) assertThat(dumpedString).contains(expected5) assertThat(dumpedString).contains(expected6) } @Test @Test fun dumpChanges_rotatesIfBufferIsFull() { fun dumpChanges_rotatesIfBufferIsFull() { lateinit var valToDump: String lateinit var valToDump: String Loading