• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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