1 /*
2  * Copyright (C) 2023 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.telephony.SubscriptionManager
21 import android.telephony.TelephonyCallback
22 import android.telephony.TelephonyManager
23 import android.util.Log
24 import com.android.settings.network.mobileDataEnabledFlow
25 import com.android.settings.wifi.WifiPickerTrackerHelper
26 import kotlinx.coroutines.Dispatchers
27 import kotlinx.coroutines.asExecutor
28 import kotlinx.coroutines.channels.ProducerScope
29 import kotlinx.coroutines.channels.awaitClose
30 import kotlinx.coroutines.flow.Flow
31 import kotlinx.coroutines.flow.callbackFlow
32 import kotlinx.coroutines.flow.catch
33 import kotlinx.coroutines.flow.conflate
34 import kotlinx.coroutines.flow.flowOf
35 import kotlinx.coroutines.flow.flowOn
36 import kotlinx.coroutines.flow.map
37 import kotlinx.coroutines.flow.onEach
38 
39 class TelephonyRepository(
40     private val context: Context,
41     private val subscriptionsChangedFlow: Flow<Unit> = context.subscriptionsChangedFlow(),
42 ) {
isMobileDataPolicyEnabledFlownull43     fun isMobileDataPolicyEnabledFlow(
44         subId: Int,
45         @TelephonyManager.MobileDataPolicy policy: Int,
46     ): Flow<Boolean> {
47         if (!SubscriptionManager.isValidSubscriptionId(subId)) return flowOf(false)
48 
49         val telephonyManager = context.telephonyManager(subId)
50 
51         return subscriptionsChangedFlow.map {
52             telephonyManager.isMobileDataPolicyEnabled(policy)
53                 .also { Log.d(TAG, "[$subId] isMobileDataPolicyEnabled($policy): $it") }
54         }.conflate().flowOn(Dispatchers.Default)
55     }
56 
setMobileDataPolicyEnablednull57     fun setMobileDataPolicyEnabled(
58         subId: Int,
59         @TelephonyManager.MobileDataPolicy policy: Int,
60         enabled: Boolean,
61     ) {
62         if (!SubscriptionManager.isValidSubscriptionId(subId)) return
63 
64         val telephonyManager = context.telephonyManager(subId)
65         Log.d(TAG, "[$subId] setMobileDataPolicyEnabled($policy): $enabled")
66         telephonyManager.setMobileDataPolicyEnabled(policy, enabled)
67     }
68 
isDataEnabledFlownull69     fun isDataEnabledFlow(subId: Int): Flow<Boolean> {
70         if (!SubscriptionManager.isValidSubscriptionId(subId)) return flowOf(false)
71 
72         return context.mobileDataEnabledFlow(subId)
73             .map {
74                 val telephonyManager = context.telephonyManager(subId)
75                 telephonyManager.isDataEnabledForReason(TelephonyManager.DATA_ENABLED_REASON_USER)
76             }
77             .catch {
78                 Log.w(TAG, "[$subId] isDataEnabledFlow: exception", it)
79                 emit(false)
80             }
81             .onEach { Log.d(TAG, "[$subId] isDataEnabledFlow: isDataEnabled() = $it") }
82             .conflate()
83             .flowOn(Dispatchers.Default)
84     }
85 
setMobileDatanull86     fun setMobileData(
87         subId: Int,
88         enabled: Boolean,
89         wifiPickerTrackerHelper: WifiPickerTrackerHelper? = null
90     ) {
91         if (!SubscriptionManager.isValidSubscriptionId(subId)) return
92 
93         Log.d(TAG, "setMobileData: $enabled")
94         MobileNetworkUtils.setMobileDataEnabled(
95             context,
96             subId,
97             enabled /* enabled */,
98             true /* disableOtherSubscriptions */
99         )
100 
101         if (wifiPickerTrackerHelper != null
102             && !wifiPickerTrackerHelper.isCarrierNetworkProvisionEnabled(subId)
103         ) {
104             wifiPickerTrackerHelper.setCarrierNetworkEnabled(enabled)
105         }
106     }
107 
108     private companion object {
109         private const val TAG = "TelephonyRepository"
110     }
111 }
112 
113 /** Creates an instance of a cold Flow for Telephony callback of given [subId]. */
telephonyCallbackFlownull114 fun <T> Context.telephonyCallbackFlow(
115     subId: Int,
116     block: ProducerScope<T>.() -> TelephonyCallback,
117 ): Flow<T> = telephonyManager(subId).telephonyCallbackFlow(block)
118 
119 /** Creates an instance of a cold Flow for Telephony callback. */
120 fun <T> TelephonyManager.telephonyCallbackFlow(
121     block: ProducerScope<T>.() -> TelephonyCallback,
122 ): Flow<T> = callbackFlow {
123     val callback = block()
124 
125     registerTelephonyCallback(Dispatchers.Default.asExecutor(), callback)
126 
127     awaitClose { unregisterTelephonyCallback(callback) }
128 }.conflate().flowOn(Dispatchers.Default)
129 
Contextnull130 fun Context.telephonyManager(subId: Int): TelephonyManager =
131     getSystemService(TelephonyManager::class.java)!!
132         .createForSubscriptionId(subId)
133