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 static android.net.ConnectivityManager.TYPE_ETHERNET; 18 19 import android.app.Activity; 20 import android.content.ComponentName; 21 import android.content.Context; 22 import android.content.Intent; 23 import android.net.ConnectivityManager; 24 import android.net.INetworkStatsSession; 25 import android.net.NetworkPolicyManager; 26 import android.net.NetworkTemplate; 27 import android.net.TrafficStats; 28 import android.net.wifi.WifiConfiguration; 29 import android.net.wifi.WifiManager; 30 import android.os.Bundle; 31 import android.os.RemoteException; 32 import android.os.SystemProperties; 33 import android.os.UserManager; 34 import android.provider.SearchIndexableResource; 35 import android.support.annotation.VisibleForTesting; 36 import android.support.v7.preference.Preference; 37 import android.support.v7.preference.PreferenceScreen; 38 import android.telephony.SubscriptionInfo; 39 import android.telephony.SubscriptionManager; 40 import android.text.BidiFormatter; 41 import android.text.Spannable; 42 import android.text.SpannableString; 43 import android.text.TextUtils; 44 import android.text.format.Formatter; 45 import android.text.style.RelativeSizeSpan; 46 import android.view.Menu; 47 import android.view.MenuInflater; 48 import android.view.MenuItem; 49 import com.android.internal.logging.nano.MetricsProto.MetricsEvent; 50 import com.android.settings.R; 51 import com.android.settings.SummaryPreference; 52 import com.android.settings.Utils; 53 import com.android.settings.dashboard.SummaryLoader; 54 import com.android.settings.search.BaseSearchIndexProvider; 55 import com.android.settings.search.Indexable; 56 import com.android.settingslib.NetworkPolicyEditor; 57 import com.android.settingslib.net.DataUsageController; 58 import java.util.ArrayList; 59 import java.util.List; 60 61 /** 62 * Settings preference fragment that displays data usage summary. 63 * 64 * This class in deprecated use {@link DataPlanUsageSummary}. 65 */ 66 @Deprecated 67 public class DataUsageSummary extends DataUsageBase implements Indexable, DataUsageEditController { 68 69 static final boolean LOGD = false; 70 71 public static final String KEY_RESTRICT_BACKGROUND = "restrict_background"; 72 73 private static final String KEY_STATUS_HEADER = "status_header"; 74 private static final String KEY_LIMIT_SUMMARY = "limit_summary"; 75 76 // Mobile data keys 77 public static final String KEY_MOBILE_USAGE_TITLE = "mobile_category"; 78 public static final String KEY_MOBILE_DATA_USAGE_TOGGLE = "data_usage_enable"; 79 public static final String KEY_MOBILE_DATA_USAGE = "cellular_data_usage"; 80 public static final String KEY_MOBILE_BILLING_CYCLE = "billing_preference"; 81 82 // Wifi keys 83 public static final String KEY_WIFI_USAGE_TITLE = "wifi_category"; 84 public static final String KEY_WIFI_DATA_USAGE = "wifi_data_usage"; 85 public static final String KEY_NETWORK_RESTRICTIONS = "network_restrictions"; 86 87 88 private DataUsageController mDataUsageController; 89 private DataUsageInfoController mDataInfoController; 90 private SummaryPreference mSummaryPreference; 91 private Preference mLimitPreference; 92 private NetworkTemplate mDefaultTemplate; 93 private int mDataUsageTemplate; 94 private NetworkRestrictionsPreference mNetworkRestrictionPreference; 95 private WifiManager mWifiManager; 96 private NetworkPolicyEditor mPolicyEditor; 97 98 @Override getHelpResource()99 protected int getHelpResource() { 100 return R.string.help_url_data_usage; 101 } 102 103 @Override onCreate(Bundle icicle)104 public void onCreate(Bundle icicle) { 105 super.onCreate(icicle); 106 107 final Context context = getContext(); 108 NetworkPolicyManager policyManager = NetworkPolicyManager.from(context); 109 mWifiManager = (WifiManager) context.getSystemService(Context.WIFI_SERVICE); 110 mPolicyEditor = new NetworkPolicyEditor(policyManager); 111 112 boolean hasMobileData = DataUsageUtils.hasMobileData(context); 113 mDataUsageController = new DataUsageController(context); 114 mDataInfoController = new DataUsageInfoController(); 115 addPreferencesFromResource(R.xml.data_usage); 116 117 int defaultSubId = DataUsageUtils.getDefaultSubscriptionId(context); 118 if (defaultSubId == SubscriptionManager.INVALID_SUBSCRIPTION_ID) { 119 hasMobileData = false; 120 } 121 mDefaultTemplate = DataUsageUtils.getDefaultTemplate(context, defaultSubId); 122 mSummaryPreference = (SummaryPreference) findPreference(KEY_STATUS_HEADER); 123 124 if (!hasMobileData || !isAdmin()) { 125 removePreference(KEY_RESTRICT_BACKGROUND); 126 } 127 if (hasMobileData) { 128 mLimitPreference = findPreference(KEY_LIMIT_SUMMARY); 129 List<SubscriptionInfo> subscriptions = 130 services.mSubscriptionManager.getActiveSubscriptionInfoList(); 131 if (subscriptions == null || subscriptions.size() == 0) { 132 addMobileSection(defaultSubId); 133 } 134 for (int i = 0; subscriptions != null && i < subscriptions.size(); i++) { 135 SubscriptionInfo subInfo = subscriptions.get(i); 136 if (subscriptions.size() > 1) { 137 addMobileSection(subInfo.getSubscriptionId(), subInfo); 138 } else { 139 addMobileSection(subInfo.getSubscriptionId()); 140 } 141 } 142 mSummaryPreference.setSelectable(true); 143 } else { 144 removePreference(KEY_LIMIT_SUMMARY); 145 mSummaryPreference.setSelectable(false); 146 } 147 boolean hasWifiRadio = DataUsageUtils.hasWifiRadio(context); 148 if (hasWifiRadio) { 149 addWifiSection(); 150 } 151 if (hasEthernet(context)) { 152 addEthernetSection(); 153 } 154 mDataUsageTemplate = hasMobileData ? R.string.cell_data_template 155 : hasWifiRadio ? R.string.wifi_data_template 156 : R.string.ethernet_data_template; 157 158 setHasOptionsMenu(true); 159 } 160 161 @Override onCreateOptionsMenu(Menu menu, MenuInflater inflater)162 public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) { 163 if (UserManager.get(getContext()).isAdminUser()) { 164 inflater.inflate(R.menu.data_usage, menu); 165 } 166 super.onCreateOptionsMenu(menu, inflater); 167 } 168 169 @Override onOptionsItemSelected(MenuItem item)170 public boolean onOptionsItemSelected(MenuItem item) { 171 switch (item.getItemId()) { 172 case R.id.data_usage_menu_cellular_networks: { 173 final Intent intent = new Intent(Intent.ACTION_MAIN); 174 intent.setComponent(new ComponentName("com.android.phone", 175 "com.android.phone.MobileNetworkSettings")); 176 startActivity(intent); 177 return true; 178 } 179 } 180 return false; 181 } 182 183 @Override onPreferenceTreeClick(Preference preference)184 public boolean onPreferenceTreeClick(Preference preference) { 185 if (preference == findPreference(KEY_STATUS_HEADER)) { 186 BillingCycleSettings.BytesEditorFragment.show(this, false); 187 return false; 188 } 189 return super.onPreferenceTreeClick(preference); 190 } 191 addMobileSection(int subId)192 private void addMobileSection(int subId) { 193 addMobileSection(subId, null); 194 } 195 addMobileSection(int subId, SubscriptionInfo subInfo)196 private void addMobileSection(int subId, SubscriptionInfo subInfo) { 197 TemplatePreferenceCategory category = (TemplatePreferenceCategory) 198 inflatePreferences(R.xml.data_usage_cellular); 199 category.setTemplate(getNetworkTemplate(subId), subId, services); 200 category.pushTemplates(services); 201 if (subInfo != null && !TextUtils.isEmpty(subInfo.getDisplayName())) { 202 Preference title = category.findPreference(KEY_MOBILE_USAGE_TITLE); 203 title.setTitle(subInfo.getDisplayName()); 204 } 205 } 206 addWifiSection()207 private void addWifiSection() { 208 TemplatePreferenceCategory category = (TemplatePreferenceCategory) 209 inflatePreferences(R.xml.data_usage_wifi); 210 category.setTemplate(NetworkTemplate.buildTemplateWifiWildcard(), 0, services); 211 mNetworkRestrictionPreference = 212 (NetworkRestrictionsPreference) category.findPreference(KEY_NETWORK_RESTRICTIONS); 213 } 214 addEthernetSection()215 private void addEthernetSection() { 216 TemplatePreferenceCategory category = (TemplatePreferenceCategory) 217 inflatePreferences(R.xml.data_usage_ethernet); 218 category.setTemplate(NetworkTemplate.buildTemplateEthernet(), 0, services); 219 } 220 inflatePreferences(int resId)221 private Preference inflatePreferences(int resId) { 222 PreferenceScreen rootPreferences = getPreferenceManager().inflateFromResource( 223 getPrefContext(), resId, null); 224 Preference pref = rootPreferences.getPreference(0); 225 rootPreferences.removeAll(); 226 227 PreferenceScreen screen = getPreferenceScreen(); 228 pref.setOrder(screen.getPreferenceCount()); 229 screen.addPreference(pref); 230 231 return pref; 232 } 233 getNetworkTemplate(int subscriptionId)234 private NetworkTemplate getNetworkTemplate(int subscriptionId) { 235 NetworkTemplate mobileAll = NetworkTemplate.buildTemplateMobileAll( 236 services.mTelephonyManager.getSubscriberId(subscriptionId)); 237 return NetworkTemplate.normalize(mobileAll, 238 services.mTelephonyManager.getMergedSubscriberIds()); 239 } 240 241 @Override onResume()242 public void onResume() { 243 super.onResume(); 244 updateState(); 245 } 246 formatTitle(Context context, String template, long usageLevel)247 private static CharSequence formatTitle(Context context, String template, long usageLevel) { 248 final float LARGER_SIZE = 1.25f * 1.25f; // (1/0.8)^2 249 final float SMALLER_SIZE = 1.0f / LARGER_SIZE; // 0.8^2 250 final int FLAGS = Spannable.SPAN_INCLUSIVE_INCLUSIVE; 251 252 final Formatter.BytesResult usedResult = Formatter.formatBytes(context.getResources(), 253 usageLevel, Formatter.FLAG_SHORTER); 254 final SpannableString enlargedValue = new SpannableString(usedResult.value); 255 enlargedValue.setSpan(new RelativeSizeSpan(LARGER_SIZE), 0, enlargedValue.length(), FLAGS); 256 257 final SpannableString amountTemplate = new SpannableString( 258 context.getString(com.android.internal.R.string.fileSizeSuffix) 259 .replace("%1$s", "^1").replace("%2$s", "^2")); 260 final CharSequence formattedUsage = TextUtils.expandTemplate(amountTemplate, 261 enlargedValue, usedResult.units); 262 263 final SpannableString fullTemplate = new SpannableString(template); 264 fullTemplate.setSpan(new RelativeSizeSpan(SMALLER_SIZE), 0, fullTemplate.length(), FLAGS); 265 return TextUtils.expandTemplate(fullTemplate, 266 BidiFormatter.getInstance().unicodeWrap(formattedUsage)); 267 } 268 updateState()269 private void updateState() { 270 DataUsageController.DataUsageInfo info = mDataUsageController.getDataUsageInfo( 271 mDefaultTemplate); 272 Context context = getContext(); 273 mDataInfoController.updateDataLimit(info, 274 services.mPolicyEditor.getPolicy(mDefaultTemplate)); 275 276 if (mSummaryPreference != null) { 277 mSummaryPreference.setTitle( 278 formatTitle(context, getString(mDataUsageTemplate), info.usageLevel)); 279 long limit = mDataInfoController.getSummaryLimit(info); 280 mSummaryPreference.setSummary(info.period); 281 282 if (limit <= 0) { 283 mSummaryPreference.setChartEnabled(false); 284 } else { 285 mSummaryPreference.setChartEnabled(true); 286 mSummaryPreference.setLabels(Formatter.formatFileSize(context, 0), 287 Formatter.formatFileSize(context, limit)); 288 mSummaryPreference.setRatios(info.usageLevel / (float) limit, 0, 289 (limit - info.usageLevel) / (float) limit); 290 } 291 } 292 if (mLimitPreference != null && (info.warningLevel > 0 || info.limitLevel > 0)) { 293 String warning = Formatter.formatFileSize(context, info.warningLevel); 294 String limit = Formatter.formatFileSize(context, info.limitLevel); 295 mLimitPreference.setSummary(getString(info.limitLevel <= 0 ? R.string.cell_warning_only 296 : R.string.cell_warning_and_limit, warning, limit)); 297 } else if (mLimitPreference != null) { 298 mLimitPreference.setSummary(null); 299 } 300 301 updateNetworkRestrictionSummary(mNetworkRestrictionPreference); 302 303 PreferenceScreen screen = getPreferenceScreen(); 304 for (int i = 1; i < screen.getPreferenceCount(); i++) { 305 ((TemplatePreferenceCategory) screen.getPreference(i)).pushTemplates(services); 306 } 307 } 308 309 @Override getMetricsCategory()310 public int getMetricsCategory() { 311 return MetricsEvent.DATA_USAGE_SUMMARY; 312 } 313 314 @Override getNetworkPolicyEditor()315 public NetworkPolicyEditor getNetworkPolicyEditor() { 316 return services.mPolicyEditor; 317 } 318 319 @Override getNetworkTemplate()320 public NetworkTemplate getNetworkTemplate() { 321 return mDefaultTemplate; 322 } 323 324 @Override updateDataUsage()325 public void updateDataUsage() { 326 updateState(); 327 } 328 329 @VisibleForTesting updateNetworkRestrictionSummary(NetworkRestrictionsPreference preference)330 void updateNetworkRestrictionSummary(NetworkRestrictionsPreference preference) { 331 if (preference == null) { 332 return; 333 } 334 mPolicyEditor.read(); 335 int count = 0; 336 for (WifiConfiguration config : mWifiManager.getConfiguredNetworks()) { 337 if (WifiConfiguration.isMetered(config, null)) { 338 count++; 339 } 340 } 341 preference.setSummary(getResources().getQuantityString( 342 R.plurals.network_restrictions_summary, count, count)); 343 } 344 345 private static class SummaryProvider 346 implements SummaryLoader.SummaryProvider { 347 348 private final Activity mActivity; 349 private final SummaryLoader mSummaryLoader; 350 private final DataUsageController mDataController; 351 SummaryProvider(Activity activity, SummaryLoader summaryLoader)352 public SummaryProvider(Activity activity, SummaryLoader summaryLoader) { 353 mActivity = activity; 354 mSummaryLoader = summaryLoader; 355 mDataController = new DataUsageController(activity); 356 } 357 358 @Override setListening(boolean listening)359 public void setListening(boolean listening) { 360 if (listening) { 361 DataUsageController.DataUsageInfo info = mDataController.getDataUsageInfo(); 362 String used; 363 if (info == null) { 364 used = Formatter.formatFileSize(mActivity, 0); 365 } else if (info.limitLevel <= 0) { 366 used = Formatter.formatFileSize(mActivity, info.usageLevel); 367 } else { 368 used = Utils.formatPercentage(info.usageLevel, info.limitLevel); 369 } 370 mSummaryLoader.setSummary(this, 371 mActivity.getString(R.string.data_usage_summary_format, used)); 372 } 373 } 374 } 375 376 public static final SummaryLoader.SummaryProviderFactory SUMMARY_PROVIDER_FACTORY 377 = SummaryProvider::new; 378 379 /** 380 * For search 381 */ 382 public static final Indexable.SearchIndexProvider SEARCH_INDEX_DATA_PROVIDER = 383 new BaseSearchIndexProvider() { 384 385 @Override 386 public List<SearchIndexableResource> getXmlResourcesToIndex(Context context, 387 boolean enabled) { 388 List<SearchIndexableResource> resources = new ArrayList<>(); 389 SearchIndexableResource resource = new SearchIndexableResource(context); 390 resource.xmlResId = R.xml.data_usage; 391 resources.add(resource); 392 393 resource = new SearchIndexableResource(context); 394 resource.xmlResId = R.xml.data_usage_cellular; 395 resources.add(resource); 396 397 resource = new SearchIndexableResource(context); 398 resource.xmlResId = R.xml.data_usage_wifi; 399 resources.add(resource); 400 401 return resources; 402 } 403 404 @Override 405 public List<String> getNonIndexableKeys(Context context) { 406 List<String> keys = super.getNonIndexableKeys(context); 407 408 if (!DataUsageUtils.hasMobileData(context)) { 409 keys.add(KEY_MOBILE_USAGE_TITLE); 410 keys.add(KEY_MOBILE_DATA_USAGE_TOGGLE); 411 keys.add(KEY_MOBILE_DATA_USAGE); 412 keys.add(KEY_MOBILE_BILLING_CYCLE); 413 } 414 415 if (!DataUsageUtils.hasWifiRadio(context)) { 416 keys.add(KEY_WIFI_DATA_USAGE); 417 keys.add(KEY_NETWORK_RESTRICTIONS); 418 } 419 420 // This title is named Wifi, and will confuse users. 421 keys.add(KEY_WIFI_USAGE_TITLE); 422 423 return keys; 424 } 425 }; 426 } 427