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