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