1 /*
<lambda>null2  * 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.datausage
18 
19 import android.app.settings.SettingsEnums
20 import android.net.NetworkPolicy
21 import android.net.NetworkTemplate
22 import android.os.Bundle
23 import android.provider.Settings
24 import android.telephony.SubscriptionManager
25 import android.util.EventLog
26 import android.util.Log
27 import android.view.View
28 import androidx.annotation.OpenForTesting
29 import androidx.annotation.VisibleForTesting
30 import androidx.fragment.app.viewModels
31 import androidx.preference.Preference
32 import com.android.settings.R
33 import com.android.settings.dashboard.DashboardFragment
34 import com.android.settings.datausage.lib.BillingCycleRepository
35 import com.android.settings.datausage.lib.NetworkUsageData
36 import com.android.settings.network.MobileNetworkRepository
37 import com.android.settings.network.SubscriptionUtil
38 import com.android.settings.network.telephony.requireSubscriptionManager
39 import com.android.settingslib.mobile.dataservice.SubscriptionInfoEntity
40 import com.android.settingslib.spa.framework.util.collectLatestWithLifecycle
41 import com.android.settingslib.spaprivileged.framework.common.userManager
42 import com.android.settingslib.utils.ThreadUtils
43 import kotlin.jvm.optionals.getOrNull
44 
45 /**
46  * Panel showing data usage history across various networks, including options
47  * to inspect based on usage cycle and control through [NetworkPolicy].
48  */
49 @OpenForTesting
50 open class DataUsageList : DashboardFragment() {
51     @VisibleForTesting
52     var template: NetworkTemplate? = null
53         private set
54 
55     @VisibleForTesting
56     var subId = SubscriptionManager.INVALID_SUBSCRIPTION_ID
57         private set
58 
59     private lateinit var billingCycleRepository: BillingCycleRepository
60 
61     private var usageAmount: Preference? = null
62     private var subscriptionInfoEntity: SubscriptionInfoEntity? = null
63     private var dataUsageListAppsController: DataUsageListAppsController? = null
64     private var chartDataUsagePreferenceController: ChartDataUsagePreferenceController? = null
65     private var dataUsageListHeaderController: DataUsageListHeaderController? = null
66 
67     private val viewModel: DataUsageListViewModel by viewModels()
68 
69     override fun getMetricsCategory() = SettingsEnums.DATA_USAGE_LIST
70 
71     override fun onCreate(savedInstanceState: Bundle?) {
72         super.onCreate(savedInstanceState)
73         billingCycleRepository = BillingCycleRepository(requireContext())
74         if (requireContext().userManager.isGuestUser) {
75             Log.e(TAG, "This setting isn't available for guest user")
76             EventLog.writeEvent(0x534e4554, "262741858", -1 /* UID */, "Guest user")
77             finish()
78             return
79         }
80         if (!billingCycleRepository.isBandwidthControlEnabled()) {
81             Log.w(TAG, "No bandwidth control; leaving")
82             finish()
83             return
84         }
85         usageAmount = findPreference(KEY_USAGE_AMOUNT)
86         processArgument()
87         val template = template
88         if (template == null) {
89             Log.e(TAG, "No template; leaving")
90             finish()
91             return
92         }
93         updateSubscriptionInfoEntity()
94         dataUsageListAppsController = use(DataUsageListAppsController::class.java).apply {
95             init(template)
96         }
97         chartDataUsagePreferenceController = use(ChartDataUsagePreferenceController::class.java)
98             .apply { init(template) }
99 
100         updateWarning()
101     }
102 
103     private fun updateWarning() {
104         val template = template ?: return
105         val warningPreference = findPreference<Preference>(KEY_WARNING)!!
106         if (template.matchRule != NetworkTemplate.MATCH_WIFI) {
107             warningPreference.setSummary(R.string.operator_warning)
108         } else if (SubscriptionUtil.isSimHardwareVisible(context)) {
109             warningPreference.setSummary(R.string.non_carrier_data_usage_warning)
110         }
111     }
112 
113     override fun onViewCreated(v: View, savedInstanceState: Bundle?) {
114         super.onViewCreated(v, savedInstanceState)
115 
116         billingCycleRepository.isModifiableFlow(subId)
117             .collectLatestWithLifecycle(viewLifecycleOwner, action = ::updatePolicy)
118 
119         val template = template ?: return
120         viewModel.templateFlow.value = template
121         dataUsageListHeaderController = DataUsageListHeaderController(
122             setPinnedHeaderView(R.layout.apps_filter_spinner),
123             template,
124             metricsCategory,
125             viewLifecycleOwner,
126             viewModel.cyclesFlow,
127             ::updateSelectedCycle,
128         )
129         viewModel.cyclesFlow.collectLatestWithLifecycle(viewLifecycleOwner) { cycles ->
130             dataUsageListAppsController?.updateCycles(cycles)
131         }
132         viewModel.chartDataFlow.collectLatestWithLifecycle(viewLifecycleOwner) { chartData ->
133             chartDataUsagePreferenceController?.update(chartData)
134         }
135     }
136 
137     override fun getPreferenceScreenResId() = R.xml.data_usage_list
138 
139     override fun getLogTag() = TAG
140 
141     private fun processArgument() {
142         arguments?.let {
143             subId = it.getInt(EXTRA_SUB_ID, SubscriptionManager.INVALID_SUBSCRIPTION_ID)
144             template = it.getParcelable(EXTRA_NETWORK_TEMPLATE, NetworkTemplate::class.java)
145         }
146         if (template == null && subId == SubscriptionManager.INVALID_SUBSCRIPTION_ID) {
147             subId = intent.getIntExtra(
148                 Settings.EXTRA_SUB_ID,
149                 SubscriptionManager.INVALID_SUBSCRIPTION_ID,
150             )
151             template = intent.getParcelableExtra(
152                 Settings.EXTRA_NETWORK_TEMPLATE,
153                 NetworkTemplate::class.java,
154             ) ?: DataUsageUtils.getMobileNetworkTemplateFromSubId(context, intent).getOrNull()
155         }
156     }
157 
158     private fun updateSubscriptionInfoEntity() {
159         ThreadUtils.postOnBackgroundThread {
160             subscriptionInfoEntity =
161                 MobileNetworkRepository.getInstance(context).getSubInfoById(subId.toString())
162         }
163     }
164 
165     /** Update chart sweeps and cycle list to reflect [NetworkPolicy] for current [template]. */
166     private fun updatePolicy(isModifiable: Boolean) {
167         val isBillingCycleModifiable = isModifiable && isActiveSubscription()
168         dataUsageListHeaderController?.setConfigButtonVisible(isBillingCycleModifiable)
169         chartDataUsagePreferenceController?.setBillingCycleModifiable(isBillingCycleModifiable)
170     }
171 
172     private fun isActiveSubscription(): Boolean =
173             requireContext().requireSubscriptionManager().getActiveSubscriptionInfo(subId) != null
174 
175     /**
176      * Updates the chart and detail data when initial loaded or selected cycle changed.
177      */
178     private fun updateSelectedCycle(usageData: NetworkUsageData) {
179         Log.d(TAG, "showing cycle $usageData")
180 
181         usageAmount?.title = usageData.getDataUsedString(requireContext()).displayText
182         viewModel.selectedCycleFlow.value = usageData
183 
184         updateApps(usageData)
185     }
186 
187     /** Updates applications data usage. */
188     private fun updateApps(usageData: NetworkUsageData) {
189         dataUsageListAppsController?.update(
190             carrierId = subscriptionInfoEntity?.carrierId,
191             startTime = usageData.startTime,
192             endTime = usageData.endTime,
193         )
194     }
195 
196     companion object {
197         const val EXTRA_SUB_ID = "sub_id"
198         const val EXTRA_NETWORK_TEMPLATE = "network_template"
199 
200         private const val TAG = "DataUsageList"
201         private const val KEY_USAGE_AMOUNT = "usage_amount"
202 
203         @VisibleForTesting
204         const val KEY_WARNING = "warning"
205     }
206 }
207