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

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

Merge "[SB Refactor] Create the callback collector object for the new pipeline...

Merge "[SB Refactor] Create the callback collector object for the new pipeline and have the processor connect to it." into tm-qpr-dev
parents effcf277 56969768
Loading
Loading
Loading
Loading
+49 −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 com.android.systemui.statusbar.pipeline.repository.NetworkCapabilityInfo
import kotlinx.coroutines.flow.StateFlow

/**
 * Interface exposing a flow for raw connectivity information. Clients should collect on
 * [rawConnectivityInfoFlow] to get updates on connectivity information.
 *
 * Note: [rawConnectivityInfoFlow] should be a *hot* flow, so that we only create one instance of it
 * and all clients get references to the same flow.
 *
 * This will be used for the new status bar pipeline to compile information we need to display some
 * of the icons in the RHS of the status bar.
 */
interface ConnectivityInfoCollector {
    val rawConnectivityInfoFlow: StateFlow<RawConnectivityInfo>
}

/**
 * An object containing all of the raw connectivity information.
 *
 * Importantly, all the information in this object should not be processed at all (i.e., the data
 * that we receive from callbacks should be piped straight into this object and not be filtered,
 * manipulated, or processed in any way). Instead, any listeners on
 * [ConnectivityInfoCollector.rawConnectivityInfoFlow] can do the processing.
 *
 * This allows us to keep all the processing in one place which is beneficial for logging and
 * debugging purposes.
 */
data class RawConnectivityInfo(
        val networkCapabilityInfo: Map<Int, NetworkCapabilityInfo> = emptyMap(),
)
+46 −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 com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.statusbar.pipeline.repository.NetworkCapabilitiesRepo
import kotlinx.coroutines.CoroutineScope
import javax.inject.Inject
import kotlinx.coroutines.flow.SharingStarted.Companion.Lazily
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.stateIn

/**
 * The real implementation of [ConnectivityInfoCollector] that will collect information from all the
 * relevant connectivity callbacks and compile it into [rawConnectivityInfoFlow].
 */
@SysUISingleton
class ConnectivityInfoCollectorImpl @Inject constructor(
        networkCapabilitiesRepo: NetworkCapabilitiesRepo,
        @Application scope: CoroutineScope,
) : ConnectivityInfoCollector {
    override val rawConnectivityInfoFlow: StateFlow<RawConnectivityInfo> =
            // TODO(b/238425913): Collect all the separate flows for individual raw information into
            //   this final flow.
            networkCapabilitiesRepo.dataStream
                    .map {
                        RawConnectivityInfo(networkCapabilityInfo = it)
                    }
                    .stateIn(scope, started = Lazily, initialValue = RawConnectivityInfo())
}
+38 −7
Original line number Diff line number Diff line
@@ -19,7 +19,15 @@ package com.android.systemui.statusbar.pipeline
import android.content.Context
import com.android.systemui.CoreStartable
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.statusbar.pipeline.repository.NetworkCapabilityInfo
import javax.inject.Inject
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.SharingStarted.Companion.Lazily
import kotlinx.coroutines.flow.emptyFlow
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.stateIn

/**
 * A processor that transforms raw connectivity information that we get from callbacks and turns it
@@ -27,20 +35,43 @@ import javax.inject.Inject
 *
 * This will be used for the new status bar pipeline to calculate the list of icons that should be
 * displayed in the RHS of the status bar.
 *
 * Anyone can listen to [processedInfoFlow] to get updates to the processed data.
 */
@SysUISingleton
class ConnectivityInfoProcessor @Inject constructor(
        connectivityInfoCollector: ConnectivityInfoCollector,
        context: Context,
        private val statusBarPipelineFlags: StatusBarPipelineFlags,
        @Application private val scope: CoroutineScope,
        statusBarPipelineFlags: StatusBarPipelineFlags,
) : CoreStartable(context) {
    // Note: This flow will not start running until a client calls `collect` on it, which means that
    // [connectivityInfoCollector]'s flow will also not start anything until that `collect` call
    // happens.
    val processedInfoFlow: Flow<ProcessedConnectivityInfo> =
            if (!statusBarPipelineFlags.isNewPipelineEnabled())
                emptyFlow()
            else connectivityInfoCollector.rawConnectivityInfoFlow
                    .map { it.process() }
                    .stateIn(
                            scope,
                            started = Lazily,
                            initialValue = ProcessedConnectivityInfo()
                    )

    override fun start() {
        if (statusBarPipelineFlags.isNewPipelineEnabled()) {
            init()
        }
    }

    /** Initializes this processor and everything it depends on. */
    private fun init() {
        // TODO(b/238425913): Register all the connectivity callbacks here.
    private fun RawConnectivityInfo.process(): ProcessedConnectivityInfo {
        // TODO(b/238425913): Actually process the raw info into meaningful data.
        return ProcessedConnectivityInfo(this.networkCapabilityInfo)
    }
}

/**
 * An object containing connectivity info that has been processed into data that can be directly
 * used by the status bar (and potentially other SysUI areas) to display icons.
 */
data class ProcessedConnectivityInfo(
        val networkCapabilityInfo: Map<Int, NetworkCapabilityInfo> = emptyMap(),
)
+7 −0
Original line number Diff line number Diff line
@@ -17,6 +17,8 @@
package com.android.systemui.statusbar.pipeline.dagger

import com.android.systemui.CoreStartable
import com.android.systemui.statusbar.pipeline.ConnectivityInfoCollector
import com.android.systemui.statusbar.pipeline.ConnectivityInfoCollectorImpl
import com.android.systemui.statusbar.pipeline.ConnectivityInfoProcessor
import dagger.Binds
import dagger.Module
@@ -30,4 +32,9 @@ abstract class StatusBarPipelineModule {
    @IntoMap
    @ClassKey(ConnectivityInfoProcessor::class)
    abstract fun bindConnectivityInfoProcessor(cip: ConnectivityInfoProcessor): CoreStartable

    @Binds
    abstract fun provideConnectivityInfoCollector(
            impl: ConnectivityInfoCollectorImpl
    ): ConnectivityInfoCollector
}
+87 −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.NetworkCapabilities
import android.testing.AndroidTestingRunner
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.statusbar.pipeline.repository.NetworkCapabilityInfo
import com.android.systemui.util.mockito.mock
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.CoroutineStart
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.InternalCoroutinesApi
import kotlinx.coroutines.cancel
import kotlinx.coroutines.flow.collect
import kotlinx.coroutines.launch
import kotlinx.coroutines.runBlocking
import kotlinx.coroutines.yield
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.Mockito.`when` as whenever

@OptIn(InternalCoroutinesApi::class)
@SmallTest
@RunWith(AndroidTestingRunner::class)
class ConnectivityInfoProcessorTest : SysuiTestCase() {

    private val statusBarPipelineFlags = mock<StatusBarPipelineFlags>()

    @Before
    fun setUp() {
        whenever(statusBarPipelineFlags.isNewPipelineEnabled()).thenReturn(true)
    }

    @Test
    fun collectorInfoUpdated_processedInfoAlsoUpdated() = runBlocking {
        // GIVEN a processor hooked up to a collector
        val scope = CoroutineScope(Dispatchers.Unconfined)
        val collector = FakeConnectivityInfoCollector()
        val processor = ConnectivityInfoProcessor(
                collector,
                context,
                scope,
                statusBarPipelineFlags,
        )

        var mostRecentValue: ProcessedConnectivityInfo? = null
        val job = launch(start = CoroutineStart.UNDISPATCHED) {
            processor.processedInfoFlow.collect {
                mostRecentValue = it
            }
        }

        // WHEN the collector emits a value
        val networkCapabilityInfo = mapOf(
                10 to NetworkCapabilityInfo(mock(), NetworkCapabilities.Builder().build())
        )
        collector.emitValue(RawConnectivityInfo(networkCapabilityInfo))
        // Because our job uses [CoroutineStart.UNDISPATCHED], it executes in the same thread as
        // this test. So, our test needs to yield to let the job run.
        // Note: Once we upgrade our Kotlin coroutines testing library, we won't need this.
        yield()

        // THEN the processor receives it
        assertThat(mostRecentValue?.networkCapabilityInfo).isEqualTo(networkCapabilityInfo)

        job.cancel()
        scope.cancel()
    }
}
Loading