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 18 19 import android.content.Context 20 import android.provider.Settings 21 import android.telephony.SubscriptionManager 22 import android.telephony.TelephonyManager 23 import android.telephony.TelephonyManager.MobileDataPolicy 24 import android.util.Log 25 import com.android.settings.wifi.WifiPickerTrackerHelper 26 import com.android.settingslib.spaprivileged.settingsprovider.settingsGlobalChangeFlow 27 import kotlinx.coroutines.Dispatchers 28 import kotlinx.coroutines.flow.Flow 29 import kotlinx.coroutines.flow.catch 30 import kotlinx.coroutines.flow.conflate 31 import kotlinx.coroutines.flow.distinctUntilChanged 32 import kotlinx.coroutines.flow.flowOf 33 import kotlinx.coroutines.flow.flowOn 34 import kotlinx.coroutines.flow.map 35 import kotlinx.coroutines.flow.merge 36 import kotlinx.coroutines.flow.onEach 37 38 class MobileDataRepository( 39 private val context: Context, 40 private val subscriptionsChangedFlow: Flow<Unit> = context.subscriptionsChangedFlow(), 41 ) { 42 fun isMobileDataPolicyEnabledFlow(subId: Int, @MobileDataPolicy policy: Int): Flow<Boolean> { 43 if (!SubscriptionManager.isValidSubscriptionId(subId)) return flowOf(false) 44 val telephonyManager = context.telephonyManager(subId) 45 return subscriptionsChangedFlow 46 .map { telephonyManager.isMobileDataPolicyEnabled(policy) } 47 .distinctUntilChanged() 48 .conflate() 49 .onEach { Log.d(TAG, "[$subId] isMobileDataPolicyEnabled($policy): $it") } 50 .flowOn(Dispatchers.Default) 51 } 52 53 fun setMobileDataPolicyEnabled(subId: Int, @MobileDataPolicy policy: Int, enabled: Boolean) { 54 if (!SubscriptionManager.isValidSubscriptionId(subId)) return 55 Log.d(TAG, "[$subId] setMobileDataPolicyEnabled($policy): $enabled") 56 context.telephonyManager(subId).setMobileDataPolicyEnabled(policy, enabled) 57 } 58 59 fun setAutoDataSwitch(subId: Int, newEnabled: Boolean) { 60 setMobileDataPolicyEnabled( 61 subId = subId, 62 policy = TelephonyManager.MOBILE_DATA_POLICY_AUTO_DATA_SWITCH, 63 enabled = newEnabled, 64 ) 65 } 66 67 /** 68 * Flow for mobile data enabled changed event. 69 * 70 * Note: This flow can only notify enabled status changes, cannot provide the latest status. 71 */ 72 fun mobileDataEnabledChangedFlow(subId: Int, sendInitialValue: Boolean = true): Flow<Unit> = 73 mobileSettingsGlobalChangedFlow(Settings.Global.MOBILE_DATA, subId, sendInitialValue) 74 75 private fun mobileSettingsGlobalChangedFlow( 76 name: String, 77 subId: Int, 78 sendInitialValue: Boolean = true, 79 ): Flow<Unit> { 80 val flow = context.settingsGlobalChangeFlow(name, sendInitialValue) 81 if (!SubscriptionManager.isValidSubscriptionId(subId)) return flow 82 val subIdFlow = 83 context.settingsGlobalChangeFlow(name = name + subId, sendInitialValue = false) 84 return merge(flow, subIdFlow) 85 } 86 87 fun isMobileDataEnabledFlow(subId: Int): Flow<Boolean> { 88 if (!SubscriptionManager.isValidSubscriptionId(subId)) return flowOf(false) 89 val telephonyManager = context.telephonyManager(subId) 90 return mobileDataEnabledChangedFlow(subId) 91 .map { 92 telephonyManager.isDataEnabledForReason(TelephonyManager.DATA_ENABLED_REASON_USER) 93 } 94 .catch { e -> 95 Log.w(TAG, "[$subId] isMobileDataEnabledFlow: exception", e) 96 emit(false) 97 } 98 .distinctUntilChanged() 99 .conflate() 100 .onEach { Log.d(TAG, "[$subId] isMobileDataEnabledFlow: $it") } 101 .flowOn(Dispatchers.Default) 102 } 103 104 fun setMobileDataEnabled( 105 subId: Int, 106 enabled: Boolean, 107 wifiPickerTrackerHelper: WifiPickerTrackerHelper? = null, 108 ) { 109 if (!SubscriptionManager.isValidSubscriptionId(subId)) return 110 111 Log.d(TAG, "setMobileDataEnabled: $enabled") 112 MobileNetworkUtils.setMobileDataEnabled( 113 context, subId, enabled, /* disableOtherSubscriptions= */ true) 114 115 if (wifiPickerTrackerHelper != null && 116 !wifiPickerTrackerHelper.isCarrierNetworkProvisionEnabled(subId)) { 117 wifiPickerTrackerHelper.setCarrierNetworkEnabled(enabled) 118 } 119 } 120 121 /** Creates an instance of a cold Flow for whether data roaming is enabled of given [subId]. */ 122 fun isDataRoamingEnabledFlow(subId: Int): Flow<Boolean> { 123 if (!SubscriptionManager.isValidSubscriptionId(subId)) return flowOf(false) 124 val telephonyManager = context.telephonyManager(subId) 125 return mobileSettingsGlobalChangedFlow(Settings.Global.DATA_ROAMING, subId) 126 .map { telephonyManager.isDataRoamingEnabled } 127 .distinctUntilChanged() 128 .conflate() 129 .onEach { Log.d(TAG, "[$subId] isDataRoamingEnabledFlow: $it") } 130 .flowOn(Dispatchers.Default) 131 } 132 133 private companion object { 134 private const val TAG = "MobileDataRepository" 135 } 136 } 137