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