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