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.statusbar.pipeline.wifi.data.repository.prod 18 19 import android.annotation.SuppressLint 20 import android.net.wifi.ScanResult 21 import android.net.wifi.WifiManager 22 import com.android.systemui.common.coroutine.ConflatedCallbackFlow.conflatedCallbackFlow 23 import com.android.systemui.dagger.qualifiers.Background 24 import com.android.systemui.dagger.qualifiers.Main 25 import com.android.systemui.log.table.TableLogBuffer 26 import com.android.systemui.log.table.logDiffsForTable 27 import com.android.systemui.statusbar.pipeline.shared.data.model.DataActivityModel 28 import com.android.systemui.statusbar.pipeline.shared.data.model.toWifiDataActivityModel 29 import com.android.systemui.statusbar.pipeline.wifi.shared.model.WifiScanEntry 30 import java.util.concurrent.Executor 31 import kotlinx.coroutines.CoroutineDispatcher 32 import kotlinx.coroutines.CoroutineScope 33 import kotlinx.coroutines.asExecutor 34 import kotlinx.coroutines.channels.awaitClose 35 import kotlinx.coroutines.flow.SharingStarted 36 import kotlinx.coroutines.flow.StateFlow 37 import kotlinx.coroutines.flow.stateIn 38 39 /** 40 * Object to provide shared helper functions between [WifiRepositoryImpl] and 41 * [WifiRepositoryViaTrackerLib]. 42 */ 43 object WifiRepositoryHelper { 44 /** Creates a flow that fetches the [DataActivityModel] from [WifiManager]. */ 45 fun createActivityFlow( 46 wifiManager: WifiManager, 47 @Main mainExecutor: Executor, 48 scope: CoroutineScope, 49 tableLogBuffer: TableLogBuffer, 50 inputLogger: (String) -> Unit, 51 ): StateFlow<DataActivityModel> { 52 return conflatedCallbackFlow { 53 val callback = 54 WifiManager.TrafficStateCallback { state -> 55 inputLogger.invoke(prettyPrintActivity(state)) 56 trySend(state.toWifiDataActivityModel()) 57 } 58 wifiManager.registerTrafficStateCallback(mainExecutor, callback) 59 awaitClose { wifiManager.unregisterTrafficStateCallback(callback) } 60 } 61 .logDiffsForTable( 62 tableLogBuffer, 63 columnPrefix = ACTIVITY_PREFIX, 64 initialValue = ACTIVITY_DEFAULT, 65 ) 66 .stateIn( 67 scope, 68 started = SharingStarted.WhileSubscribed(), 69 initialValue = ACTIVITY_DEFAULT, 70 ) 71 } 72 73 /** 74 * Creates a flow that listens for new [ScanResult]s from [WifiManager]. Does not request a scan 75 */ 76 fun createNetworkScanFlow( 77 wifiManager: WifiManager, 78 scope: CoroutineScope, 79 @Background dispatcher: CoroutineDispatcher, 80 inputLogger: () -> Unit, 81 ): StateFlow<List<WifiScanEntry>> { 82 return conflatedCallbackFlow { 83 val callback = 84 object : WifiManager.ScanResultsCallback() { 85 @SuppressLint("MissingPermission") 86 override fun onScanResultsAvailable() { 87 inputLogger.invoke() 88 trySend(wifiManager.scanResults.toModel()) 89 } 90 } 91 92 wifiManager.registerScanResultsCallback(dispatcher.asExecutor(), callback) 93 94 awaitClose { wifiManager.unregisterScanResultsCallback(callback) } 95 } 96 .stateIn(scope, SharingStarted.Eagerly, emptyList()) 97 } 98 99 private fun List<ScanResult>.toModel(): List<WifiScanEntry> = map { WifiScanEntry(it.SSID) } 100 101 // TODO(b/292534484): This print should only be done in [MessagePrinter] part of the log buffer. 102 private fun prettyPrintActivity(activity: Int): String { 103 return when (activity) { 104 WifiManager.TrafficStateCallback.DATA_ACTIVITY_NONE -> "NONE" 105 WifiManager.TrafficStateCallback.DATA_ACTIVITY_IN -> "IN" 106 WifiManager.TrafficStateCallback.DATA_ACTIVITY_OUT -> "OUT" 107 WifiManager.TrafficStateCallback.DATA_ACTIVITY_INOUT -> "INOUT" 108 else -> "INVALID" 109 } 110 } 111 112 private const val ACTIVITY_PREFIX = "wifiActivity" 113 val ACTIVITY_DEFAULT = DataActivityModel(hasActivityIn = false, hasActivityOut = false) 114 } 115