Home | History | Annotate | Download | only in datausage
      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
     99     protected int getHelpResource() {
    100         return R.string.help_url_data_usage;
    101     }
    102 
    103     @Override
    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
    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
    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
    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 
    192     private void addMobileSection(int subId) {
    193         addMobileSection(subId, null);
    194     }
    195 
    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 
    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 
    215     private void addEthernetSection() {
    216         TemplatePreferenceCategory category = (TemplatePreferenceCategory)
    217                 inflatePreferences(R.xml.data_usage_ethernet);
    218         category.setTemplate(NetworkTemplate.buildTemplateEthernet(), 0, services);
    219     }
    220 
    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 
    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
    242     public void onResume() {
    243         super.onResume();
    244         updateState();
    245     }
    246 
    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 
    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
    310     public int getMetricsCategory() {
    311         return MetricsEvent.DATA_USAGE_SUMMARY;
    312     }
    313 
    314     @Override
    315     public NetworkPolicyEditor getNetworkPolicyEditor() {
    316         return services.mPolicyEditor;
    317     }
    318 
    319     @Override
    320     public NetworkTemplate getNetworkTemplate() {
    321         return mDefaultTemplate;
    322     }
    323 
    324     @Override
    325     public void updateDataUsage() {
    326         updateState();
    327     }
    328 
    329     @VisibleForTesting
    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 
    352         public SummaryProvider(Activity activity, SummaryLoader summaryLoader) {
    353             mActivity = activity;
    354             mSummaryLoader = summaryLoader;
    355             mDataController = new DataUsageController(activity);
    356         }
    357 
    358         @Override
    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