1 /* 2 * Copyright (C) 2016 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file 5 * except in compliance with the License. You may obtain a copy of the License at 6 * 7 * http://www.apache.org/licenses/LICENSE-2.0 8 * 9 * Unless required by applicable law or agreed to in writing, software distributed under the 10 * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 11 * KIND, either express or implied. See the License for the specific language governing 12 * permissions and limitations under the License. 13 */ 14 15 package com.android.settings.datausage; 16 17 import android.app.Activity; 18 import android.app.settings.SettingsEnums; 19 import android.content.Context; 20 import android.net.NetworkTemplate; 21 import android.os.Bundle; 22 import android.os.UserManager; 23 import android.telephony.SubscriptionInfo; 24 import android.telephony.SubscriptionManager; 25 import android.text.BidiFormatter; 26 import android.text.Spannable; 27 import android.text.SpannableString; 28 import android.text.TextUtils; 29 import android.text.format.Formatter; 30 import android.text.style.RelativeSizeSpan; 31 import android.util.EventLog; 32 import android.util.Log; 33 34 import androidx.annotation.VisibleForTesting; 35 import androidx.preference.Preference; 36 import androidx.preference.PreferenceScreen; 37 38 import com.android.settings.R; 39 import com.android.settings.datausage.lib.DataUsageLib; 40 import com.android.settings.network.ProxySubscriptionManager; 41 import com.android.settings.network.SubscriptionUtil; 42 import com.android.settingslib.NetworkPolicyEditor; 43 import com.android.settingslib.core.AbstractPreferenceController; 44 45 import java.util.ArrayList; 46 import java.util.List; 47 48 /** 49 * Settings preference fragment that displays data usage summary. 50 */ 51 public class DataUsageSummary extends DataUsageBaseFragment implements DataUsageEditController { 52 53 private static final String TAG = "DataUsageSummary"; 54 55 static final boolean LOGD = false; 56 57 public static final String KEY_RESTRICT_BACKGROUND = "restrict_background"; 58 59 private static final String KEY_STATUS_HEADER = "status_header"; 60 61 // Mobile data keys 62 public static final String KEY_MOBILE_USAGE_TITLE = "mobile_category"; 63 public static final String KEY_MOBILE_DATA_USAGE_TOGGLE = "data_usage_enable"; 64 public static final String KEY_MOBILE_DATA_USAGE = "cellular_data_usage"; 65 public static final String KEY_MOBILE_BILLING_CYCLE = "billing_preference"; 66 67 // Wifi keys 68 public static final String KEY_WIFI_USAGE_TITLE = "wifi_category"; 69 public static final String KEY_WIFI_DATA_USAGE = "wifi_data_usage"; 70 71 private DataUsageSummaryPreference mSummaryPreference; 72 private DataUsageSummaryPreferenceController mSummaryController; 73 private NetworkTemplate mDefaultTemplate; 74 private ProxySubscriptionManager mProxySubscriptionMgr; 75 76 @Override getHelpResource()77 public int getHelpResource() { 78 return R.string.help_url_data_usage; 79 } 80 isSimHardwareVisible(Context context)81 public boolean isSimHardwareVisible(Context context) { 82 return SubscriptionUtil.isSimHardwareVisible(context); 83 } 84 85 @Override onCreate(Bundle icicle)86 public void onCreate(Bundle icicle) { 87 super.onCreate(icicle); 88 Context context = getContext(); 89 if (isGuestUser(context)) { 90 Log.e(TAG, "This setting isn't available due to user restriction."); 91 EventLog.writeEvent(0x534e4554, "262243574", -1 /* UID */, "Guest user"); 92 finish(); 93 return; 94 } 95 96 if (!isSimHardwareVisible(context)) { 97 finish(); 98 return; 99 } 100 enableProxySubscriptionManager(context); 101 102 boolean hasMobileData = DataUsageUtils.hasMobileData(context); 103 104 final int defaultSubId = SubscriptionManager.getDefaultDataSubscriptionId(); 105 if (defaultSubId == SubscriptionManager.INVALID_SUBSCRIPTION_ID) { 106 hasMobileData = false; 107 } 108 mDefaultTemplate = DataUsageUtils.getDefaultTemplate(context, defaultSubId); 109 mSummaryPreference = findPreference(KEY_STATUS_HEADER); 110 111 if (!hasMobileData || !isAdmin()) { 112 removePreference(KEY_RESTRICT_BACKGROUND); 113 } 114 boolean hasWifiRadio = DataUsageUtils.hasWifiRadio(context); 115 if (hasMobileData) { 116 addMobileSection(defaultSubId); 117 if (hasActiveSubscription() && hasWifiRadio) { 118 // If the device has active SIM, the data usage section shows usage for mobile, 119 // and the WiFi section is added if there is a WiFi radio - legacy behavior. 120 addWifiSection(); 121 } 122 // Do not add the WiFi section if either there is no WiFi radio (obviously) or if no 123 // SIM is installed. In the latter case the data usage section will show WiFi usage and 124 // there should be no explicit WiFi section added. 125 } else if (hasWifiRadio) { 126 addWifiSection(); 127 } 128 if (DataUsageUtils.hasEthernet(context)) { 129 addEthernetSection(); 130 } 131 setHasOptionsMenu(true); 132 } 133 134 @Override onPreferenceTreeClick(Preference preference)135 public boolean onPreferenceTreeClick(Preference preference) { 136 if (preference == findPreference(KEY_STATUS_HEADER)) { 137 BillingCycleSettings.BytesEditorFragment.show(this, false); 138 return false; 139 } 140 return super.onPreferenceTreeClick(preference); 141 } 142 143 @Override getPreferenceScreenResId()144 protected int getPreferenceScreenResId() { 145 return R.xml.data_usage; 146 } 147 148 @Override getLogTag()149 protected String getLogTag() { 150 return TAG; 151 } 152 153 @Override createPreferenceControllers(Context context)154 protected List<AbstractPreferenceController> createPreferenceControllers(Context context) { 155 final Activity activity = getActivity(); 156 final ArrayList<AbstractPreferenceController> controllers = new ArrayList<>(); 157 if (!isSimHardwareVisible(context)) { 158 return controllers; 159 } 160 mSummaryController = 161 new DataUsageSummaryPreferenceController(activity, getSettingsLifecycle(), this, 162 DataUsageUtils.getDefaultSubscriptionId(activity)); 163 controllers.add(mSummaryController); 164 getSettingsLifecycle().addObserver(mSummaryController); 165 return controllers; 166 } 167 168 @VisibleForTesting addMobileSection(int subId)169 void addMobileSection(int subId) { 170 addMobileSection(subId, null); 171 } 172 173 @VisibleForTesting enableProxySubscriptionManager(Context context)174 void enableProxySubscriptionManager(Context context) { 175 // Enable ProxySubscriptionMgr with Lifecycle support for all controllers 176 // live within this fragment 177 mProxySubscriptionMgr = ProxySubscriptionManager.getInstance(context); 178 mProxySubscriptionMgr.setLifecycle(getLifecycle()); 179 } 180 181 @VisibleForTesting hasActiveSubscription()182 boolean hasActiveSubscription() { 183 final List<SubscriptionInfo> subInfoList = 184 mProxySubscriptionMgr.getActiveSubscriptionsInfo(); 185 return ((subInfoList != null) && (subInfoList.size() > 0)); 186 } 187 addMobileSection(int subId, SubscriptionInfo subInfo)188 private void addMobileSection(int subId, SubscriptionInfo subInfo) { 189 TemplatePreferenceCategory category = (TemplatePreferenceCategory) 190 inflatePreferences(R.xml.data_usage_cellular); 191 category.setTemplate(DataUsageLib.getMobileTemplate(getContext(), subId), 192 subId, services); 193 category.pushTemplates(services); 194 final CharSequence displayName = SubscriptionUtil.getUniqueSubscriptionDisplayName( 195 subInfo, getContext()); 196 if (subInfo != null && !TextUtils.isEmpty(displayName)) { 197 Preference title = category.findPreference(KEY_MOBILE_USAGE_TITLE); 198 title.setTitle(displayName); 199 } 200 } 201 202 @VisibleForTesting addWifiSection()203 void addWifiSection() { 204 TemplatePreferenceCategory category = (TemplatePreferenceCategory) 205 inflatePreferences(R.xml.data_usage_wifi); 206 category.setTemplate(new NetworkTemplate.Builder(NetworkTemplate.MATCH_WIFI).build(), 207 0, services); 208 } 209 addEthernetSection()210 private void addEthernetSection() { 211 TemplatePreferenceCategory category = (TemplatePreferenceCategory) 212 inflatePreferences(R.xml.data_usage_ethernet); 213 category.setTemplate(new NetworkTemplate.Builder(NetworkTemplate.MATCH_ETHERNET).build(), 214 0, services); 215 } 216 inflatePreferences(int resId)217 private Preference inflatePreferences(int resId) { 218 PreferenceScreen rootPreferences = getPreferenceManager().inflateFromResource( 219 getPrefContext(), resId, null); 220 Preference pref = rootPreferences.getPreference(0); 221 rootPreferences.removeAll(); 222 223 PreferenceScreen screen = getPreferenceScreen(); 224 pref.setOrder(screen.getPreferenceCount()); 225 screen.addPreference(pref); 226 227 return pref; 228 } 229 230 @Override onResume()231 public void onResume() { 232 super.onResume(); 233 updateState(); 234 } 235 236 @VisibleForTesting formatUsage(Context context, String template, long usageLevel)237 static CharSequence formatUsage(Context context, String template, long usageLevel) { 238 final float LARGER_SIZE = 1.25f * 1.25f; // (1/0.8)^2 239 final float SMALLER_SIZE = 1.0f / LARGER_SIZE; // 0.8^2 240 return formatUsage(context, template, usageLevel, LARGER_SIZE, SMALLER_SIZE); 241 } 242 formatUsage(Context context, String template, long usageLevel, float larger, float smaller)243 static CharSequence formatUsage(Context context, String template, long usageLevel, 244 float larger, float smaller) { 245 final int FLAGS = Spannable.SPAN_INCLUSIVE_INCLUSIVE; 246 247 final Formatter.BytesResult usedResult = Formatter.formatBytes(context.getResources(), 248 usageLevel, Formatter.FLAG_CALCULATE_ROUNDED | Formatter.FLAG_IEC_UNITS); 249 final SpannableString enlargedValue = new SpannableString(usedResult.value); 250 enlargedValue.setSpan(new RelativeSizeSpan(larger), 0, enlargedValue.length(), FLAGS); 251 252 final SpannableString amountTemplate = new SpannableString( 253 context.getString(com.android.internal.R.string.fileSizeSuffix) 254 .replace("%1$s", "^1").replace("%2$s", "^2")); 255 final CharSequence formattedUsage = TextUtils.expandTemplate(amountTemplate, 256 enlargedValue, usedResult.units); 257 258 final SpannableString fullTemplate = new SpannableString(template); 259 fullTemplate.setSpan(new RelativeSizeSpan(smaller), 0, fullTemplate.length(), FLAGS); 260 return TextUtils.expandTemplate(fullTemplate, 261 BidiFormatter.getInstance().unicodeWrap(formattedUsage.toString())); 262 } 263 updateState()264 private void updateState() { 265 PreferenceScreen screen = getPreferenceScreen(); 266 for (int i = 1; i < screen.getPreferenceCount(); i++) { 267 Preference currentPreference = screen.getPreference(i); 268 if (currentPreference instanceof TemplatePreferenceCategory) { 269 ((TemplatePreferenceCategory) currentPreference).pushTemplates(services); 270 } 271 } 272 } 273 274 @Override getMetricsCategory()275 public int getMetricsCategory() { 276 return SettingsEnums.DATA_USAGE_SUMMARY; 277 } 278 279 @Override getNetworkPolicyEditor()280 public NetworkPolicyEditor getNetworkPolicyEditor() { 281 return services.mPolicyEditor; 282 } 283 284 @Override getNetworkTemplate()285 public NetworkTemplate getNetworkTemplate() { 286 return mDefaultTemplate; 287 } 288 289 @Override updateDataUsage()290 public void updateDataUsage() { 291 updateState(); 292 mSummaryController.updateState(mSummaryPreference); 293 } 294 isGuestUser(Context context)295 private static boolean isGuestUser(Context context) { 296 if (context == null) return false; 297 final UserManager userManager = context.getSystemService(UserManager.class); 298 if (userManager == null) return false; 299 return userManager.isGuestUser(); 300 } 301 } 302