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

Commit b88b8764 authored by Android Build Coastguard Worker's avatar Android Build Coastguard Worker
Browse files

Merge cherrypicks of ['googleplex-android-review.googlesource.com/29590947'] into 24Q4-release.

Change-Id: Iae17a95aabc1bbeea4eedd04f7ffa70c0d31620e
parents 12db316e e9964f3c
Loading
Loading
Loading
Loading
+56 −14
Original line number Diff line number Diff line
@@ -23,11 +23,13 @@ import android.util.Log
import androidx.lifecycle.LifecycleOwner
import com.android.settings.network.SubscriptionUtil
import com.android.settingslib.spa.framework.util.collectLatestWithLifecycle
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.asExecutor
import kotlinx.coroutines.channels.awaitClose
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.callbackFlow
import kotlinx.coroutines.flow.conflate
import kotlinx.coroutines.flow.distinctUntilChanged
@@ -36,6 +38,8 @@ import kotlinx.coroutines.flow.flowOf
import kotlinx.coroutines.flow.flowOn
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.onEach
import kotlinx.coroutines.flow.onStart
import kotlinx.coroutines.flow.shareIn

private const val TAG = "SubscriptionRepository"

@@ -132,20 +136,7 @@ class SubscriptionRepository(private val context: Context) {
    fun canDisablePhysicalSubscription() = subscriptionManager.canDisablePhysicalSubscription()

    /** Flow for subscriptions changes. */
    fun subscriptionsChangedFlow() = callbackFlow {
        val listener = object : SubscriptionManager.OnSubscriptionsChangedListener() {
            override fun onSubscriptionsChanged() {
                trySend(Unit)
            }
        }

        subscriptionManager.addOnSubscriptionsChangedListener(
            Dispatchers.Default.asExecutor(),
            listener,
        )

        awaitClose { subscriptionManager.removeOnSubscriptionsChangedListener(listener) }
    }.conflate().onEach { Log.d(TAG, "subscriptions changed") }.flowOn(Dispatchers.Default)
    fun subscriptionsChangedFlow() = getSharedSubscriptionsChangedFlow(context)

    /** Flow of active subscription ids. */
    fun activeSubscriptionIdListFlow(): Flow<List<Int>> =
@@ -172,6 +163,57 @@ class SubscriptionRepository(private val context: Context) {
                flowOf(null)
            }
        }

    companion object {
        private lateinit var SharedSubscriptionsChangedFlow: Flow<Unit>

        private fun getSharedSubscriptionsChangedFlow(context: Context): Flow<Unit> {
            if (!this::SharedSubscriptionsChangedFlow.isInitialized) {
                SharedSubscriptionsChangedFlow =
                    context.applicationContext
                        .requireSubscriptionManager()
                        .subscriptionsChangedFlow()
                        .shareIn(
                            scope = CoroutineScope(Dispatchers.Default),
                            started = SharingStarted.WhileSubscribed(),
                            replay = 1,
                        )
            }
            return SharedSubscriptionsChangedFlow
        }

        /**
         * Flow for subscriptions changes.
         *
         * Note: Even the SubscriptionManager.addOnSubscriptionsChangedListener's doc says the
         * SubscriptionManager.OnSubscriptionsChangedListener.onSubscriptionsChanged() method will
         * also be invoked once initially when calling it, there still case that the
         * onSubscriptionsChanged() method is not invoked initially. For example, when the
         * onSubscriptionsChanged event never happens before, on a device never ever has any
         * subscriptions.
         */
        private fun SubscriptionManager.subscriptionsChangedFlow() =
            callbackFlow {
                    val listener =
                        object : SubscriptionManager.OnSubscriptionsChangedListener() {
                            override fun onSubscriptionsChanged() {
                                trySend(Unit)
                            }

                            override fun onAddListenerFailed() {
                                close()
                            }
                        }

                    addOnSubscriptionsChangedListener(Dispatchers.Default.asExecutor(), listener)

                    awaitClose { removeOnSubscriptionsChangedListener(listener) }
                }
                .onStart { emit(Unit) } // Ensure this flow is never empty
                .conflate()
                .onEach { Log.d(TAG, "subscriptions changed") }
                .flowOn(Dispatchers.Default)
    }
}

val Context.subscriptionManager: SubscriptionManager?
+5 −0
Original line number Diff line number Diff line
@@ -62,10 +62,15 @@ class MobileNetworkSwitchControllerTest {
        on { isSubscriptionEnabledFlow(SUB_ID) } doReturn flowOf(false)
    }

    private val mockSubscriptionActivationRepository = mock<SubscriptionActivationRepository> {
        on { isActivationChangeableFlow() } doReturn flowOf(true)
    }

    private val controller = MobileNetworkSwitchController(
        context = context,
        preferenceKey = TEST_KEY,
        subscriptionRepository = mockSubscriptionRepository,
        subscriptionActivationRepository = mockSubscriptionActivationRepository,
    ).apply { init(SUB_ID) }

    @Test
+17 −1
Original line number Diff line number Diff line
@@ -91,7 +91,23 @@ class SubscriptionRepositoryTest {

        subInfoListener?.onSubscriptionsChanged()

        assertThat(listDeferred.await()).hasSize(2)
        assertThat(listDeferred.await().size).isAtLeast(2)
    }

    @Test
    fun subscriptionsChangedFlow_managerNotCallOnSubscriptionsChangedInitially() = runBlocking {
        mockSubscriptionManager.stub {
            on { addOnSubscriptionsChangedListener(any(), any()) } doAnswer
                {
                    subInfoListener =
                        it.arguments[1] as SubscriptionManager.OnSubscriptionsChangedListener
                    // not call onSubscriptionsChanged here
                }
        }

        val initialValue = repository.subscriptionsChangedFlow().firstWithTimeoutOrNull()

        assertThat(initialValue).isSameInstanceAs(Unit)
    }

    @Test