Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 4ca3922a authored by TreeHugger Robot's avatar TreeHugger Robot Committed by Android (Google) Code Review
Browse files

Merge "Flow-based connectivity listener" into tm-qpr-dev

parents 0471f7b8 06af71dc
Loading
Loading
Loading
Loading
+10 −0
Original line number Diff line number Diff line
@@ -255,6 +255,16 @@ public class LogModule {
        return factory.create("MediaCarouselCtlrLog", 20);
    }

    /**
     * Provides a {@link LogBuffer} for use in the status bar connectivity pipeline
     */
    @Provides
    @SysUISingleton
    @StatusBarConnectivityLog
    public static LogBuffer provideStatusBarConnectivityBuffer(LogBufferFactory factory) {
        return factory.create("StatusBarConnectivityLog", 64);
    }

    /** Allows logging buffers to be tweaked via adb on debug builds but not on prod builds. */
    @Provides
    @SysUISingleton
+36 −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.log.dagger;

import static java.lang.annotation.RetentionPolicy.RUNTIME;

import com.android.systemui.log.LogBuffer;
import com.android.systemui.statusbar.pipeline.ConnectivityInfoProcessor;

import java.lang.annotation.Documented;
import java.lang.annotation.Retention;

import javax.inject.Qualifier;

/**
 * A {@link LogBuffer} for events processed by {@link ConnectivityInfoProcessor}
 */
@Qualifier
@Documented
@Retention(RUNTIME)
public @interface StatusBarConnectivityLog {
}
+59 −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.statusbar.pipeline

import android.net.Network
import android.net.NetworkCapabilities
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.log.LogBuffer
import com.android.systemui.log.LogLevel
import com.android.systemui.log.dagger.StatusBarConnectivityLog
import javax.inject.Inject

@SysUISingleton
class ConnectivityPipelineLogger @Inject constructor(
    @StatusBarConnectivityLog private val buffer: LogBuffer,
) {
    fun logOnCapabilitiesChanged(network: Network, networkCapabilities: NetworkCapabilities) {
        buffer.log(
            TAG,
            LogLevel.INFO,
            {
                int1 = network.getNetId()
                str1 = networkCapabilities.toString()
            },
            {
                "onCapabilitiesChanged: net=$int1 capabilities=$str1"
            }
        )
    }

    fun logOnLost(network: Network) {
        buffer.log(
            TAG,
            LogLevel.INFO,
            {
                int1 = network.getNetId()
            },
            {
                "onLost: net=$int1"
            }
        )
    }
}

private const val TAG = "SbConnectivityPipeline"
+92 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2021 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.
 */

@file:OptIn(ExperimentalCoroutinesApi::class)

package com.android.systemui.statusbar.pipeline.repository

import android.annotation.SuppressLint
import android.net.ConnectivityManager
import android.net.Network
import android.net.NetworkCapabilities
import android.net.NetworkRequest
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.statusbar.pipeline.ConnectivityPipelineLogger
import javax.inject.Inject
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.channels.awaitClose
import kotlinx.coroutines.flow.SharingStarted.Companion.Lazily
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.callbackFlow
import kotlinx.coroutines.flow.stateIn

/** Repository that contains all relevant [NetworkCapabilites] for the current networks */
@SysUISingleton
class NetworkCapabilitiesRepo @Inject constructor(
    connectivityManager: ConnectivityManager,
    @Application scope: CoroutineScope,
    logger: ConnectivityPipelineLogger,
) {
    @SuppressLint("MissingPermission")
    val dataStream: StateFlow<Map<Int, NetworkCapabilityInfo>> = run {
        var state = emptyMap<Int, NetworkCapabilityInfo>()
        callbackFlow {
                val networkRequest: NetworkRequest =
                    NetworkRequest.Builder()
                        .clearCapabilities()
                        .addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_VPN)
                        .addTransportType(NetworkCapabilities.TRANSPORT_WIFI)
                        .addTransportType(NetworkCapabilities.TRANSPORT_CELLULAR)
                        .build()
                val callback =
                    // TODO (b/240569788): log these using [LogBuffer]
                    object : ConnectivityManager.NetworkCallback(FLAG_INCLUDE_LOCATION_INFO) {
                        override fun onCapabilitiesChanged(
                            network: Network,
                            networkCapabilities: NetworkCapabilities
                        ) {
                            logger.logOnCapabilitiesChanged(network, networkCapabilities)
                            state =
                                state.toMutableMap().also {
                                    it[network.getNetId()] =
                                        NetworkCapabilityInfo(network, networkCapabilities)
                                }
                            trySend(state)
                        }

                        override fun onLost(network: Network) {
                            logger.logOnLost(network)
                            state = state.toMutableMap().also { it.remove(network.getNetId()) }
                            trySend(state)
                        }
                    }
                connectivityManager.registerNetworkCallback(networkRequest, callback)

                awaitClose { connectivityManager.unregisterNetworkCallback(callback) }
            }
            .stateIn(scope, started = Lazily, initialValue = state)
    }
}

/** contains info about network capabilities. */
data class NetworkCapabilityInfo(
    val network: Network,
    val capabilities: NetworkCapabilities,
)

private const val TAG = "ConnectivityRepository"
+75 −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.statusbar.pipeline

import android.net.Network
import android.net.NetworkCapabilities
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.dump.DumpManager
import com.android.systemui.log.LogBufferFactory
import com.android.systemui.log.LogcatEchoTracker
import com.google.common.truth.Truth.assertThat
import java.io.PrintWriter
import java.io.StringWriter
import org.junit.Test
import org.mockito.Mockito
import org.mockito.Mockito.mock

@SmallTest
class ConnectivityPipelineLoggerTest : SysuiTestCase() {
    private val buffer = LogBufferFactory(DumpManager(), mock(LogcatEchoTracker::class.java))
        .create("buffer", 10)
    private val logger = ConnectivityPipelineLogger(buffer)

    @Test
    fun testLogNetworkCapsChange_bufferHasInfo() {
        logger.logOnCapabilitiesChanged(NET_1, NET_1_CAPS)

        val stringWriter = StringWriter()
        buffer.dump(PrintWriter(stringWriter), tailLength = 0)
        val actualString = stringWriter.toString()

        val expectedNetId = NET_1_ID.toString()
        val expectedCaps = NET_1_CAPS.toString()

        assertThat(actualString).contains(expectedNetId)
        assertThat(actualString).contains(expectedCaps)
    }

    @Test
    fun testLogOnLost_bufferHasNetIdOfLostNetwork() {
        logger.logOnLost(NET_1)

        val stringWriter = StringWriter()
        buffer.dump(PrintWriter(stringWriter), tailLength = 0)
        val actualString = stringWriter.toString()

        val expectedNetId = NET_1_ID.toString()

        assertThat(actualString).contains(expectedNetId)
    }

    private val NET_1_ID = 100
    private val NET_1 = com.android.systemui.util.mockito.mock<Network>().also {
        Mockito.`when`(it.getNetId()).thenReturn(NET_1_ID)
    }
    private val NET_1_CAPS = NetworkCapabilities.Builder()
        .addTransportType(NetworkCapabilities.TRANSPORT_CELLULAR)
        .addCapability(NetworkCapabilities.NET_CAPABILITY_VALIDATED)
        .build()
}
Loading