1 /*
<lambda>null2  * Copyright (C) 2024 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.settings.network.telephony.wificalling
18 
19 import android.app.Application
20 import android.app.settings.SettingsEnums
21 import android.telephony.CarrierConfigManager
22 import android.telephony.SubscriptionManager
23 import android.telephony.TelephonyManager
24 import androidx.lifecycle.AndroidViewModel
25 import androidx.lifecycle.viewModelScope
26 import com.android.settings.R
27 import com.android.settings.network.mobileDataEnabledFlow
28 import com.android.settings.network.telephony.ims.ImsMmTelRepositoryImpl
29 import com.android.settings.network.telephony.requireSubscriptionManager
30 import com.android.settings.network.telephony.safeGetConfig
31 import com.android.settings.network.telephony.subscriptionsChangedFlow
32 import com.android.settings.network.telephony.telephonyManager
33 import com.android.settings.overlay.FeatureFactory.Companion.featureFactory
34 import kotlinx.coroutines.Dispatchers
35 import kotlinx.coroutines.ExperimentalCoroutinesApi
36 import kotlinx.coroutines.channels.Channel
37 import kotlinx.coroutines.flow.distinctUntilChanged
38 import kotlinx.coroutines.flow.flatMapLatest
39 import kotlinx.coroutines.flow.launchIn
40 import kotlinx.coroutines.flow.map
41 import kotlinx.coroutines.flow.merge
42 import kotlinx.coroutines.flow.onEach
43 import kotlinx.coroutines.flow.receiveAsFlow
44 import kotlinx.coroutines.plus
45 
46 @OptIn(ExperimentalCoroutinesApi::class)
47 class CrossSimCallingViewModel(
48     private val application: Application,
49 ) : AndroidViewModel(application) {
50 
51     private val subscriptionManager = application.requireSubscriptionManager()
52     private val carrierConfigManager =
53         application.getSystemService(CarrierConfigManager::class.java)!!
54     private val scope = viewModelScope + Dispatchers.Default
55     private val metricsFeatureProvider = featureFactory.metricsFeatureProvider
56     private val updateChannel = Channel<Unit>()
57 
58     init {
59         val resources = application.resources
60         if (resources.getBoolean(R.bool.config_auto_data_switch_enables_cross_sim_calling)) {
61             application.subscriptionsChangedFlow()
62                 .flatMapLatest {
63                     val activeSubIds = subscriptionManager.activeSubscriptionIdList.toList()
64                     merge(
65                         activeSubIds.anyMobileDataEnableChangedFlow(),
66                         updateChannel.receiveAsFlow(),
67                     ).map {
68                         activeSubIds to crossSimCallNewEnabled(activeSubIds)
69                     }
70                 }
71                 .distinctUntilChanged()
72                 .onEach { (activeSubIds, newEnabled) ->
73                     updateCrossSimCalling(activeSubIds, newEnabled)
74                 }
75                 .launchIn(scope)
76         }
77     }
78 
79     fun updateCrossSimCalling() {
80         updateChannel.trySend(Unit)
81     }
82 
83     private fun List<Int>.anyMobileDataEnableChangedFlow() = map { subId ->
84         application.mobileDataEnabledFlow(subId = subId, sendInitialValue = false)
85     }.merge()
86 
87     private suspend fun updateCrossSimCalling(activeSubIds: List<Int>, newEnabled: Boolean) {
88         metricsFeatureProvider.action(
89             application,
90             SettingsEnums.ACTION_UPDATE_CROSS_SIM_CALLING_ON_AUTO_DATA_SWITCH_EVENT,
91             newEnabled,
92         )
93         activeSubIds.filter { crossSimAvailable(it) }.forEach { subId ->
94             ImsMmTelRepositoryImpl(application, subId)
95                 .setCrossSimCallingEnabled(newEnabled)
96         }
97     }
98 
99     private suspend fun crossSimAvailable(subId: Int): Boolean =
100         WifiCallingRepository(application, subId).isWifiCallingSupported() &&
101             crossSimImsAvailable(subId)
102 
103     private fun crossSimImsAvailable(subId: Int): Boolean =
104         carrierConfigManager.safeGetConfig(
105             keys = listOf(CarrierConfigManager.KEY_CARRIER_CROSS_SIM_IMS_AVAILABLE_BOOL),
106             subId = subId,
107         ).getBoolean(CarrierConfigManager.KEY_CARRIER_CROSS_SIM_IMS_AVAILABLE_BOOL, false)
108 
109     private fun crossSimCallNewEnabled(activeSubscriptionIdList: List<Int>): Boolean {
110         val defaultDataSubId = SubscriptionManager.getDefaultDataSubscriptionId()
111         return SubscriptionManager.isValidSubscriptionId(defaultDataSubId) &&
112             activeSubscriptionIdList.any { subId ->
113                 subId != defaultDataSubId &&
114                     application.telephonyManager(subId).isMobileDataPolicyEnabled(
115                         TelephonyManager.MOBILE_DATA_POLICY_AUTO_DATA_SWITCH
116                     )
117             }
118     }
119 }
120