Home | History | Annotate | Download | only in settings
      1 /*
      2  * Copyright (C) 2011 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;
     18 
     19 import static android.net.ConnectivityManager.TYPE_ETHERNET;
     20 import static android.net.ConnectivityManager.TYPE_MOBILE;
     21 import static android.net.ConnectivityManager.TYPE_WIFI;
     22 import static android.net.ConnectivityManager.TYPE_WIMAX;
     23 import static android.net.NetworkPolicy.LIMIT_DISABLED;
     24 import static android.net.NetworkPolicy.WARNING_DISABLED;
     25 import static android.net.NetworkPolicyManager.EXTRA_NETWORK_TEMPLATE;
     26 import static android.net.NetworkPolicyManager.POLICY_NONE;
     27 import static android.net.NetworkPolicyManager.POLICY_REJECT_METERED_BACKGROUND;
     28 import static android.net.NetworkPolicyManager.computeLastCycleBoundary;
     29 import static android.net.NetworkPolicyManager.computeNextCycleBoundary;
     30 import static android.net.NetworkTemplate.MATCH_MOBILE_3G_LOWER;
     31 import static android.net.NetworkTemplate.MATCH_MOBILE_4G;
     32 import static android.net.NetworkTemplate.MATCH_MOBILE_ALL;
     33 import static android.net.NetworkTemplate.MATCH_WIFI;
     34 import static android.net.NetworkTemplate.buildTemplateEthernet;
     35 import static android.net.NetworkTemplate.buildTemplateMobile3gLower;
     36 import static android.net.NetworkTemplate.buildTemplateMobile4g;
     37 import static android.net.NetworkTemplate.buildTemplateMobileAll;
     38 import static android.net.NetworkTemplate.buildTemplateWifiWildcard;
     39 import static android.net.TrafficStats.GB_IN_BYTES;
     40 import static android.net.TrafficStats.MB_IN_BYTES;
     41 import static android.net.TrafficStats.UID_REMOVED;
     42 import static android.net.TrafficStats.UID_TETHERING;
     43 import static android.telephony.TelephonyManager.SIM_STATE_READY;
     44 import static android.text.format.DateUtils.FORMAT_ABBREV_MONTH;
     45 import static android.text.format.DateUtils.FORMAT_SHOW_DATE;
     46 import static android.view.ViewGroup.LayoutParams.WRAP_CONTENT;
     47 import static com.android.internal.util.Preconditions.checkNotNull;
     48 import static com.android.settings.Utils.prepareCustomPreferencesList;
     49 
     50 import android.animation.LayoutTransition;
     51 import android.app.ActivityManager;
     52 import android.app.AlertDialog;
     53 import android.app.Dialog;
     54 import android.app.DialogFragment;
     55 import android.app.Fragment;
     56 import android.app.FragmentManager;
     57 import android.app.FragmentTransaction;
     58 import android.app.LoaderManager.LoaderCallbacks;
     59 import android.content.ContentResolver;
     60 import android.content.Context;
     61 import android.content.DialogInterface;
     62 import android.content.Intent;
     63 import android.content.Loader;
     64 import android.content.SharedPreferences;
     65 import android.content.pm.PackageManager;
     66 import android.content.res.Resources;
     67 import android.graphics.Color;
     68 import android.graphics.drawable.ColorDrawable;
     69 import android.graphics.drawable.Drawable;
     70 import android.net.ConnectivityManager;
     71 import android.net.INetworkPolicyManager;
     72 import android.net.INetworkStatsService;
     73 import android.net.INetworkStatsSession;
     74 import android.net.NetworkPolicy;
     75 import android.net.NetworkPolicyManager;
     76 import android.net.NetworkStats;
     77 import android.net.NetworkStatsHistory;
     78 import android.net.NetworkTemplate;
     79 import android.net.TrafficStats;
     80 import android.os.AsyncTask;
     81 import android.os.Bundle;
     82 import android.os.INetworkManagementService;
     83 import android.os.Parcel;
     84 import android.os.Parcelable;
     85 import android.os.RemoteException;
     86 import android.os.ServiceManager;
     87 import android.os.SystemProperties;
     88 import android.os.UserHandle;
     89 import android.preference.Preference;
     90 import android.preference.PreferenceActivity;
     91 import android.provider.Settings;
     92 import android.telephony.TelephonyManager;
     93 import android.text.TextUtils;
     94 import android.text.format.DateUtils;
     95 import android.text.format.Formatter;
     96 import android.text.format.Time;
     97 import android.util.Log;
     98 import android.util.SparseArray;
     99 import android.util.SparseBooleanArray;
    100 import android.view.LayoutInflater;
    101 import android.view.Menu;
    102 import android.view.MenuInflater;
    103 import android.view.MenuItem;
    104 import android.view.View;
    105 import android.view.View.OnClickListener;
    106 import android.view.ViewGroup;
    107 import android.widget.AdapterView;
    108 import android.widget.AdapterView.OnItemClickListener;
    109 import android.widget.AdapterView.OnItemSelectedListener;
    110 import android.widget.ArrayAdapter;
    111 import android.widget.BaseAdapter;
    112 import android.widget.Button;
    113 import android.widget.CheckBox;
    114 import android.widget.CompoundButton;
    115 import android.widget.CompoundButton.OnCheckedChangeListener;
    116 import android.widget.ImageView;
    117 import android.widget.LinearLayout;
    118 import android.widget.ListView;
    119 import android.widget.NumberPicker;
    120 import android.widget.ProgressBar;
    121 import android.widget.Spinner;
    122 import android.widget.Switch;
    123 import android.widget.TabHost;
    124 import android.widget.TabHost.OnTabChangeListener;
    125 import android.widget.TabHost.TabContentFactory;
    126 import android.widget.TabHost.TabSpec;
    127 import android.widget.TabWidget;
    128 import android.widget.TextView;
    129 
    130 import com.android.internal.telephony.PhoneConstants;
    131 import com.android.settings.drawable.InsetBoundsDrawable;
    132 import com.android.settings.net.ChartData;
    133 import com.android.settings.net.ChartDataLoader;
    134 import com.android.settings.net.DataUsageMeteredSettings;
    135 import com.android.settings.net.NetworkPolicyEditor;
    136 import com.android.settings.net.SummaryForAllUidLoader;
    137 import com.android.settings.net.UidDetail;
    138 import com.android.settings.net.UidDetailProvider;
    139 import com.android.settings.widget.ChartDataUsageView;
    140 import com.android.settings.widget.ChartDataUsageView.DataUsageChartListener;
    141 import com.android.settings.widget.PieChartView;
    142 import com.google.android.collect.Lists;
    143 
    144 import libcore.util.Objects;
    145 
    146 import java.util.ArrayList;
    147 import java.util.Collections;
    148 import java.util.List;
    149 import java.util.Locale;
    150 
    151 /**
    152  * Panel showing data usage history across various networks, including options
    153  * to inspect based on usage cycle and control through {@link NetworkPolicy}.
    154  */
    155 public class DataUsageSummary extends Fragment {
    156     private static final String TAG = "DataUsage";
    157     private static final boolean LOGD = false;
    158 
    159     // TODO: remove this testing code
    160     private static final boolean TEST_ANIM = false;
    161     private static final boolean TEST_RADIOS = false;
    162 
    163     private static final String TEST_RADIOS_PROP = "test.radios";
    164     private static final String TEST_SUBSCRIBER_PROP = "test.subscriberid";
    165 
    166     private static final String TAB_3G = "3g";
    167     private static final String TAB_4G = "4g";
    168     private static final String TAB_MOBILE = "mobile";
    169     private static final String TAB_WIFI = "wifi";
    170     private static final String TAB_ETHERNET = "ethernet";
    171 
    172     private static final String TAG_CONFIRM_DATA_DISABLE = "confirmDataDisable";
    173     private static final String TAG_CONFIRM_DATA_ROAMING = "confirmDataRoaming";
    174     private static final String TAG_CONFIRM_LIMIT = "confirmLimit";
    175     private static final String TAG_CYCLE_EDITOR = "cycleEditor";
    176     private static final String TAG_WARNING_EDITOR = "warningEditor";
    177     private static final String TAG_LIMIT_EDITOR = "limitEditor";
    178     private static final String TAG_CONFIRM_RESTRICT = "confirmRestrict";
    179     private static final String TAG_DENIED_RESTRICT = "deniedRestrict";
    180     private static final String TAG_CONFIRM_APP_RESTRICT = "confirmAppRestrict";
    181     private static final String TAG_CONFIRM_AUTO_SYNC_CHANGE = "confirmAutoSyncChange";
    182     private static final String TAG_APP_DETAILS = "appDetails";
    183 
    184     private static final int LOADER_CHART_DATA = 2;
    185     private static final int LOADER_SUMMARY = 3;
    186 
    187     private INetworkManagementService mNetworkService;
    188     private INetworkStatsService mStatsService;
    189     private NetworkPolicyManager mPolicyManager;
    190     private ConnectivityManager mConnService;
    191 
    192     private INetworkStatsSession mStatsSession;
    193 
    194     private static final String PREF_FILE = "data_usage";
    195     private static final String PREF_SHOW_WIFI = "show_wifi";
    196     private static final String PREF_SHOW_ETHERNET = "show_ethernet";
    197 
    198     private SharedPreferences mPrefs;
    199 
    200     private TabHost mTabHost;
    201     private ViewGroup mTabsContainer;
    202     private TabWidget mTabWidget;
    203     private ListView mListView;
    204     private DataUsageAdapter mAdapter;
    205 
    206     /** Distance to inset content from sides, when needed. */
    207     private int mInsetSide = 0;
    208 
    209     private ViewGroup mHeader;
    210 
    211     private ViewGroup mNetworkSwitchesContainer;
    212     private LinearLayout mNetworkSwitches;
    213     private Switch mDataEnabled;
    214     private View mDataEnabledView;
    215     private CheckBox mDisableAtLimit;
    216     private View mDisableAtLimitView;
    217 
    218     private View mCycleView;
    219     private Spinner mCycleSpinner;
    220     private CycleAdapter mCycleAdapter;
    221 
    222     private ChartDataUsageView mChart;
    223     private TextView mUsageSummary;
    224     private TextView mEmpty;
    225 
    226     private View mAppDetail;
    227     private ImageView mAppIcon;
    228     private ViewGroup mAppTitles;
    229     private PieChartView mAppPieChart;
    230     private TextView mAppForeground;
    231     private TextView mAppBackground;
    232     private Button mAppSettings;
    233 
    234     private LinearLayout mAppSwitches;
    235     private CheckBox mAppRestrict;
    236     private View mAppRestrictView;
    237 
    238     private boolean mShowWifi = false;
    239     private boolean mShowEthernet = false;
    240 
    241     private NetworkTemplate mTemplate;
    242     private ChartData mChartData;
    243 
    244     private AppItem mCurrentApp = null;
    245 
    246     private Intent mAppSettingsIntent;
    247 
    248     private NetworkPolicyEditor mPolicyEditor;
    249 
    250     private String mCurrentTab = null;
    251     private String mIntentTab = null;
    252 
    253     private MenuItem mMenuDataRoaming;
    254     private MenuItem mMenuRestrictBackground;
    255     private MenuItem mMenuAutoSync;
    256 
    257     /** Flag used to ignore listeners during binding. */
    258     private boolean mBinding;
    259 
    260     private UidDetailProvider mUidDetailProvider;
    261 
    262     @Override
    263     public void onCreate(Bundle savedInstanceState) {
    264         super.onCreate(savedInstanceState);
    265         final Context context = getActivity();
    266 
    267         mNetworkService = INetworkManagementService.Stub.asInterface(
    268                 ServiceManager.getService(Context.NETWORKMANAGEMENT_SERVICE));
    269         mStatsService = INetworkStatsService.Stub.asInterface(
    270                 ServiceManager.getService(Context.NETWORK_STATS_SERVICE));
    271         mPolicyManager = NetworkPolicyManager.from(context);
    272         mConnService = ConnectivityManager.from(context);
    273 
    274         mPrefs = getActivity().getSharedPreferences(PREF_FILE, Context.MODE_PRIVATE);
    275 
    276         mPolicyEditor = new NetworkPolicyEditor(mPolicyManager);
    277         mPolicyEditor.read();
    278 
    279         try {
    280             if (!mNetworkService.isBandwidthControlEnabled()) {
    281                 Log.w(TAG, "No bandwidth control; leaving");
    282                 getActivity().finish();
    283             }
    284         } catch (RemoteException e) {
    285             Log.w(TAG, "No bandwidth control; leaving");
    286             getActivity().finish();
    287         }
    288 
    289         try {
    290             mStatsSession = mStatsService.openSession();
    291         } catch (RemoteException e) {
    292             throw new RuntimeException(e);
    293         }
    294 
    295         mShowWifi = mPrefs.getBoolean(PREF_SHOW_WIFI, false);
    296         mShowEthernet = mPrefs.getBoolean(PREF_SHOW_ETHERNET, false);
    297 
    298         // override preferences when no mobile radio
    299         if (!hasReadyMobileRadio(context)) {
    300             mShowWifi = true;
    301             mShowEthernet = true;
    302         }
    303 
    304         setHasOptionsMenu(true);
    305     }
    306 
    307     @Override
    308     public View onCreateView(LayoutInflater inflater, ViewGroup container,
    309             Bundle savedInstanceState) {
    310 
    311         final Context context = inflater.getContext();
    312         final View view = inflater.inflate(R.layout.data_usage_summary, container, false);
    313 
    314         mUidDetailProvider = new UidDetailProvider(context);
    315 
    316         mTabHost = (TabHost) view.findViewById(android.R.id.tabhost);
    317         mTabsContainer = (ViewGroup) view.findViewById(R.id.tabs_container);
    318         mTabWidget = (TabWidget) view.findViewById(android.R.id.tabs);
    319         mListView = (ListView) view.findViewById(android.R.id.list);
    320 
    321         // decide if we need to manually inset our content, or if we should rely
    322         // on parent container for inset.
    323         final boolean shouldInset = mListView.getScrollBarStyle()
    324                 == View.SCROLLBARS_OUTSIDE_OVERLAY;
    325         mInsetSide = 0;
    326 
    327         // adjust padding around tabwidget as needed
    328         prepareCustomPreferencesList(container, view, mListView, false);
    329 
    330         mTabHost.setup();
    331         mTabHost.setOnTabChangedListener(mTabListener);
    332 
    333         mHeader = (ViewGroup) inflater.inflate(R.layout.data_usage_header, mListView, false);
    334         mHeader.setClickable(true);
    335 
    336         mListView.addHeaderView(new View(context), null, true);
    337         mListView.addHeaderView(mHeader, null, true);
    338         mListView.setItemsCanFocus(true);
    339 
    340         if (mInsetSide > 0) {
    341             // inset selector and divider drawables
    342             insetListViewDrawables(mListView, mInsetSide);
    343             mHeader.setPaddingRelative(mInsetSide, 0, mInsetSide, 0);
    344         }
    345 
    346         {
    347             // bind network switches
    348             mNetworkSwitchesContainer = (ViewGroup) mHeader.findViewById(
    349                     R.id.network_switches_container);
    350             mNetworkSwitches = (LinearLayout) mHeader.findViewById(R.id.network_switches);
    351 
    352             mDataEnabled = new Switch(inflater.getContext());
    353             mDataEnabledView = inflatePreference(inflater, mNetworkSwitches, mDataEnabled);
    354             mDataEnabled.setOnCheckedChangeListener(mDataEnabledListener);
    355             mNetworkSwitches.addView(mDataEnabledView);
    356 
    357             mDisableAtLimit = new CheckBox(inflater.getContext());
    358             mDisableAtLimit.setClickable(false);
    359             mDisableAtLimit.setFocusable(false);
    360             mDisableAtLimitView = inflatePreference(inflater, mNetworkSwitches, mDisableAtLimit);
    361             mDisableAtLimitView.setClickable(true);
    362             mDisableAtLimitView.setFocusable(true);
    363             mDisableAtLimitView.setOnClickListener(mDisableAtLimitListener);
    364             mNetworkSwitches.addView(mDisableAtLimitView);
    365         }
    366 
    367         // bind cycle dropdown
    368         mCycleView = mHeader.findViewById(R.id.cycles);
    369         mCycleSpinner = (Spinner) mCycleView.findViewById(R.id.cycles_spinner);
    370         mCycleAdapter = new CycleAdapter(context);
    371         mCycleSpinner.setAdapter(mCycleAdapter);
    372         mCycleSpinner.setOnItemSelectedListener(mCycleListener);
    373 
    374         mChart = (ChartDataUsageView) mHeader.findViewById(R.id.chart);
    375         mChart.setListener(mChartListener);
    376         mChart.bindNetworkPolicy(null);
    377 
    378         {
    379             // bind app detail controls
    380             mAppDetail = mHeader.findViewById(R.id.app_detail);
    381             mAppIcon = (ImageView) mAppDetail.findViewById(R.id.app_icon);
    382             mAppTitles = (ViewGroup) mAppDetail.findViewById(R.id.app_titles);
    383             mAppPieChart = (PieChartView) mAppDetail.findViewById(R.id.app_pie_chart);
    384             mAppForeground = (TextView) mAppDetail.findViewById(R.id.app_foreground);
    385             mAppBackground = (TextView) mAppDetail.findViewById(R.id.app_background);
    386             mAppSwitches = (LinearLayout) mAppDetail.findViewById(R.id.app_switches);
    387 
    388             mAppSettings = (Button) mAppDetail.findViewById(R.id.app_settings);
    389             mAppSettings.setOnClickListener(mAppSettingsListener);
    390 
    391             mAppRestrict = new CheckBox(inflater.getContext());
    392             mAppRestrict.setClickable(false);
    393             mAppRestrict.setFocusable(false);
    394             mAppRestrictView = inflatePreference(inflater, mAppSwitches, mAppRestrict);
    395             mAppRestrictView.setClickable(true);
    396             mAppRestrictView.setFocusable(true);
    397             mAppRestrictView.setOnClickListener(mAppRestrictListener);
    398             mAppSwitches.addView(mAppRestrictView);
    399         }
    400 
    401         mUsageSummary = (TextView) mHeader.findViewById(R.id.usage_summary);
    402         mEmpty = (TextView) mHeader.findViewById(android.R.id.empty);
    403 
    404         mAdapter = new DataUsageAdapter(mUidDetailProvider, mInsetSide);
    405         mListView.setOnItemClickListener(mListListener);
    406         mListView.setAdapter(mAdapter);
    407 
    408         return view;
    409     }
    410 
    411     @Override
    412     public void onResume() {
    413         super.onResume();
    414 
    415         // pick default tab based on incoming intent
    416         final Intent intent = getActivity().getIntent();
    417         mIntentTab = computeTabFromIntent(intent);
    418 
    419         // this kicks off chain reaction which creates tabs, binds the body to
    420         // selected network, and binds chart, cycles and detail list.
    421         updateTabs();
    422 
    423         // kick off background task to update stats
    424         new AsyncTask<Void, Void, Void>() {
    425             @Override
    426             protected Void doInBackground(Void... params) {
    427                 try {
    428                     // wait a few seconds before kicking off
    429                     Thread.sleep(2 * DateUtils.SECOND_IN_MILLIS);
    430                     mStatsService.forceUpdate();
    431                 } catch (InterruptedException e) {
    432                 } catch (RemoteException e) {
    433                 }
    434                 return null;
    435             }
    436 
    437             @Override
    438             protected void onPostExecute(Void result) {
    439                 if (isAdded()) {
    440                     updateBody();
    441                 }
    442             }
    443         }.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
    444     }
    445 
    446     @Override
    447     public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
    448         inflater.inflate(R.menu.data_usage, menu);
    449     }
    450 
    451     @Override
    452     public void onPrepareOptionsMenu(Menu menu) {
    453         final Context context = getActivity();
    454         final boolean appDetailMode = isAppDetailMode();
    455         final boolean isOwner = ActivityManager.getCurrentUser() == UserHandle.USER_OWNER;
    456 
    457         mMenuDataRoaming = menu.findItem(R.id.data_usage_menu_roaming);
    458         mMenuDataRoaming.setVisible(hasReadyMobileRadio(context) && !appDetailMode);
    459         mMenuDataRoaming.setChecked(getDataRoaming());
    460 
    461         mMenuRestrictBackground = menu.findItem(R.id.data_usage_menu_restrict_background);
    462         mMenuRestrictBackground.setVisible(
    463                 hasReadyMobileRadio(context) && isOwner && !appDetailMode);
    464         mMenuRestrictBackground.setChecked(mPolicyManager.getRestrictBackground());
    465 
    466         mMenuAutoSync = menu.findItem(R.id.data_usage_menu_auto_sync);
    467         mMenuAutoSync.setChecked(ContentResolver.getMasterSyncAutomatically());
    468         mMenuAutoSync.setVisible(!appDetailMode);
    469 
    470         final MenuItem split4g = menu.findItem(R.id.data_usage_menu_split_4g);
    471         split4g.setVisible(hasReadyMobile4gRadio(context) && isOwner && !appDetailMode);
    472         split4g.setChecked(isMobilePolicySplit());
    473 
    474         final MenuItem showWifi = menu.findItem(R.id.data_usage_menu_show_wifi);
    475         if (hasWifiRadio(context) && hasReadyMobileRadio(context)) {
    476             showWifi.setVisible(!appDetailMode);
    477             showWifi.setChecked(mShowWifi);
    478         } else {
    479             showWifi.setVisible(false);
    480         }
    481 
    482         final MenuItem showEthernet = menu.findItem(R.id.data_usage_menu_show_ethernet);
    483         if (hasEthernet(context) && hasReadyMobileRadio(context)) {
    484             showEthernet.setVisible(!appDetailMode);
    485             showEthernet.setChecked(mShowEthernet);
    486         } else {
    487             showEthernet.setVisible(false);
    488         }
    489 
    490         final MenuItem metered = menu.findItem(R.id.data_usage_menu_metered);
    491         if (hasReadyMobileRadio(context) || hasWifiRadio(context)) {
    492             metered.setVisible(!appDetailMode);
    493         } else {
    494             metered.setVisible(false);
    495         }
    496 
    497         final MenuItem help = menu.findItem(R.id.data_usage_menu_help);
    498         String helpUrl;
    499         if (!TextUtils.isEmpty(helpUrl = getResources().getString(R.string.help_url_data_usage))) {
    500             HelpUtils.prepareHelpMenuItem(context, help, helpUrl);
    501         } else {
    502             help.setVisible(false);
    503         }
    504     }
    505 
    506     @Override
    507     public boolean onOptionsItemSelected(MenuItem item) {
    508         switch (item.getItemId()) {
    509             case R.id.data_usage_menu_roaming: {
    510                 final boolean dataRoaming = !item.isChecked();
    511                 if (dataRoaming) {
    512                     ConfirmDataRoamingFragment.show(this);
    513                 } else {
    514                     // no confirmation to disable roaming
    515                     setDataRoaming(false);
    516                 }
    517                 return true;
    518             }
    519             case R.id.data_usage_menu_restrict_background: {
    520                 final boolean restrictBackground = !item.isChecked();
    521                 if (restrictBackground) {
    522                     ConfirmRestrictFragment.show(this);
    523                 } else {
    524                     // no confirmation to drop restriction
    525                     setRestrictBackground(false);
    526                 }
    527                 return true;
    528             }
    529             case R.id.data_usage_menu_split_4g: {
    530                 final boolean mobileSplit = !item.isChecked();
    531                 setMobilePolicySplit(mobileSplit);
    532                 item.setChecked(isMobilePolicySplit());
    533                 updateTabs();
    534                 return true;
    535             }
    536             case R.id.data_usage_menu_show_wifi: {
    537                 mShowWifi = !item.isChecked();
    538                 mPrefs.edit().putBoolean(PREF_SHOW_WIFI, mShowWifi).apply();
    539                 item.setChecked(mShowWifi);
    540                 updateTabs();
    541                 return true;
    542             }
    543             case R.id.data_usage_menu_show_ethernet: {
    544                 mShowEthernet = !item.isChecked();
    545                 mPrefs.edit().putBoolean(PREF_SHOW_ETHERNET, mShowEthernet).apply();
    546                 item.setChecked(mShowEthernet);
    547                 updateTabs();
    548                 return true;
    549             }
    550             case R.id.data_usage_menu_metered: {
    551                 final PreferenceActivity activity = (PreferenceActivity) getActivity();
    552                 activity.startPreferencePanel(DataUsageMeteredSettings.class.getCanonicalName(), null,
    553                         R.string.data_usage_metered_title, null, this, 0);
    554                 return true;
    555             }
    556             case R.id.data_usage_menu_auto_sync: {
    557                 if (ActivityManager.isUserAMonkey()) {
    558                     Log.d("SyncState", "ignoring monkey's attempt to flip global sync state");
    559                 } else {
    560                     ConfirmAutoSyncChangeFragment.show(this, !item.isChecked());
    561                 }
    562                 return true;
    563             }
    564         }
    565         return false;
    566     }
    567 
    568     @Override
    569     public void onDestroy() {
    570         mDataEnabledView = null;
    571         mDisableAtLimitView = null;
    572 
    573         mUidDetailProvider.clearCache();
    574         mUidDetailProvider = null;
    575 
    576         TrafficStats.closeQuietly(mStatsSession);
    577 
    578         if (this.isRemoving()) {
    579             getFragmentManager()
    580                     .popBackStack(TAG_APP_DETAILS, FragmentManager.POP_BACK_STACK_INCLUSIVE);
    581         }
    582 
    583         super.onDestroy();
    584     }
    585 
    586     /**
    587      * Build and assign {@link LayoutTransition} to various containers. Should
    588      * only be assigned after initial layout is complete.
    589      */
    590     private void ensureLayoutTransitions() {
    591         // skip when already setup
    592         if (mChart.getLayoutTransition() != null) return;
    593 
    594         mTabsContainer.setLayoutTransition(buildLayoutTransition());
    595         mHeader.setLayoutTransition(buildLayoutTransition());
    596         mNetworkSwitchesContainer.setLayoutTransition(buildLayoutTransition());
    597 
    598         final LayoutTransition chartTransition = buildLayoutTransition();
    599         chartTransition.disableTransitionType(LayoutTransition.APPEARING);
    600         chartTransition.disableTransitionType(LayoutTransition.DISAPPEARING);
    601         mChart.setLayoutTransition(chartTransition);
    602     }
    603 
    604     private static LayoutTransition buildLayoutTransition() {
    605         final LayoutTransition transition = new LayoutTransition();
    606         if (TEST_ANIM) {
    607             transition.setDuration(1500);
    608         }
    609         transition.setAnimateParentHierarchy(false);
    610         return transition;
    611     }
    612 
    613     /**
    614      * Rebuild all tabs based on {@link NetworkPolicyEditor} and
    615      * {@link #mShowWifi}, hiding the tabs entirely when applicable. Selects
    616      * first tab, and kicks off a full rebind of body contents.
    617      */
    618     private void updateTabs() {
    619         final Context context = getActivity();
    620         mTabHost.clearAllTabs();
    621 
    622         final boolean mobileSplit = isMobilePolicySplit();
    623         if (mobileSplit && hasReadyMobile4gRadio(context)) {
    624             mTabHost.addTab(buildTabSpec(TAB_3G, R.string.data_usage_tab_3g));
    625             mTabHost.addTab(buildTabSpec(TAB_4G, R.string.data_usage_tab_4g));
    626         } else if (hasReadyMobileRadio(context)) {
    627             mTabHost.addTab(buildTabSpec(TAB_MOBILE, R.string.data_usage_tab_mobile));
    628         }
    629         if (mShowWifi && hasWifiRadio(context)) {
    630             mTabHost.addTab(buildTabSpec(TAB_WIFI, R.string.data_usage_tab_wifi));
    631         }
    632         if (mShowEthernet && hasEthernet(context)) {
    633             mTabHost.addTab(buildTabSpec(TAB_ETHERNET, R.string.data_usage_tab_ethernet));
    634         }
    635 
    636         final boolean noTabs = mTabWidget.getTabCount() == 0;
    637         final boolean multipleTabs = mTabWidget.getTabCount() > 1;
    638         mTabWidget.setVisibility(multipleTabs ? View.VISIBLE : View.GONE);
    639         if (mIntentTab != null) {
    640             if (Objects.equal(mIntentTab, mTabHost.getCurrentTabTag())) {
    641                 // already hit updateBody() when added; ignore
    642                 updateBody();
    643             } else {
    644                 mTabHost.setCurrentTabByTag(mIntentTab);
    645             }
    646             mIntentTab = null;
    647         } else if (noTabs) {
    648             // no usable tabs, so hide body
    649             updateBody();
    650         } else {
    651             // already hit updateBody() when added; ignore
    652         }
    653     }
    654 
    655     /**
    656      * Factory that provide empty {@link View} to make {@link TabHost} happy.
    657      */
    658     private TabContentFactory mEmptyTabContent = new TabContentFactory() {
    659         @Override
    660         public View createTabContent(String tag) {
    661             return new View(mTabHost.getContext());
    662         }
    663     };
    664 
    665     /**
    666      * Build {@link TabSpec} with thin indicator, and empty content.
    667      */
    668     private TabSpec buildTabSpec(String tag, int titleRes) {
    669         return mTabHost.newTabSpec(tag).setIndicator(getText(titleRes)).setContent(
    670                 mEmptyTabContent);
    671     }
    672 
    673     private OnTabChangeListener mTabListener = new OnTabChangeListener() {
    674         @Override
    675         public void onTabChanged(String tabId) {
    676             // user changed tab; update body
    677             updateBody();
    678         }
    679     };
    680 
    681     /**
    682      * Update body content based on current tab. Loads
    683      * {@link NetworkStatsHistory} and {@link NetworkPolicy} from system, and
    684      * binds them to visible controls.
    685      */
    686     private void updateBody() {
    687         mBinding = true;
    688         if (!isAdded()) return;
    689 
    690         final Context context = getActivity();
    691         final String currentTab = mTabHost.getCurrentTabTag();
    692         final boolean isOwner = ActivityManager.getCurrentUser() == UserHandle.USER_OWNER;
    693 
    694         if (currentTab == null) {
    695             Log.w(TAG, "no tab selected; hiding body");
    696             mListView.setVisibility(View.GONE);
    697             return;
    698         } else {
    699             mListView.setVisibility(View.VISIBLE);
    700         }
    701 
    702         final boolean tabChanged = !currentTab.equals(mCurrentTab);
    703         mCurrentTab = currentTab;
    704 
    705         if (LOGD) Log.d(TAG, "updateBody() with currentTab=" + currentTab);
    706 
    707         mDataEnabledView.setVisibility(isOwner ? View.VISIBLE : View.GONE);
    708 
    709         // TODO: remove mobile tabs when SIM isn't ready
    710         final TelephonyManager tele = TelephonyManager.from(context);
    711 
    712         if (TAB_MOBILE.equals(currentTab)) {
    713             setPreferenceTitle(mDataEnabledView, R.string.data_usage_enable_mobile);
    714             setPreferenceTitle(mDisableAtLimitView, R.string.data_usage_disable_mobile_limit);
    715             mTemplate = buildTemplateMobileAll(getActiveSubscriberId(context));
    716 
    717         } else if (TAB_3G.equals(currentTab)) {
    718             setPreferenceTitle(mDataEnabledView, R.string.data_usage_enable_3g);
    719             setPreferenceTitle(mDisableAtLimitView, R.string.data_usage_disable_3g_limit);
    720             // TODO: bind mDataEnabled to 3G radio state
    721             mTemplate = buildTemplateMobile3gLower(getActiveSubscriberId(context));
    722 
    723         } else if (TAB_4G.equals(currentTab)) {
    724             setPreferenceTitle(mDataEnabledView, R.string.data_usage_enable_4g);
    725             setPreferenceTitle(mDisableAtLimitView, R.string.data_usage_disable_4g_limit);
    726             // TODO: bind mDataEnabled to 4G radio state
    727             mTemplate = buildTemplateMobile4g(getActiveSubscriberId(context));
    728 
    729         } else if (TAB_WIFI.equals(currentTab)) {
    730             // wifi doesn't have any controls
    731             mDataEnabledView.setVisibility(View.GONE);
    732             mDisableAtLimitView.setVisibility(View.GONE);
    733             mTemplate = buildTemplateWifiWildcard();
    734 
    735         } else if (TAB_ETHERNET.equals(currentTab)) {
    736             // ethernet doesn't have any controls
    737             mDataEnabledView.setVisibility(View.GONE);
    738             mDisableAtLimitView.setVisibility(View.GONE);
    739             mTemplate = buildTemplateEthernet();
    740 
    741         } else {
    742             throw new IllegalStateException("unknown tab: " + currentTab);
    743         }
    744 
    745         // kick off loader for network history
    746         // TODO: consider chaining two loaders together instead of reloading
    747         // network history when showing app detail.
    748         getLoaderManager().restartLoader(LOADER_CHART_DATA,
    749                 ChartDataLoader.buildArgs(mTemplate, mCurrentApp), mChartDataCallbacks);
    750 
    751         // detail mode can change visible menus, invalidate
    752         getActivity().invalidateOptionsMenu();
    753 
    754         mBinding = false;
    755     }
    756 
    757     private boolean isAppDetailMode() {
    758         return mCurrentApp != null;
    759     }
    760 
    761     /**
    762      * Update UID details panels to match {@link #mCurrentApp}, showing or
    763      * hiding them depending on {@link #isAppDetailMode()}.
    764      */
    765     private void updateAppDetail() {
    766         final Context context = getActivity();
    767         final PackageManager pm = context.getPackageManager();
    768         final LayoutInflater inflater = getActivity().getLayoutInflater();
    769 
    770         if (isAppDetailMode()) {
    771             mAppDetail.setVisibility(View.VISIBLE);
    772             mCycleAdapter.setChangeVisible(false);
    773         } else {
    774             mAppDetail.setVisibility(View.GONE);
    775             mCycleAdapter.setChangeVisible(true);
    776 
    777             // hide detail stats when not in detail mode
    778             mChart.bindDetailNetworkStats(null);
    779             return;
    780         }
    781 
    782         // remove warning/limit sweeps while in detail mode
    783         mChart.bindNetworkPolicy(null);
    784 
    785         // show icon and all labels appearing under this app
    786         final int uid = mCurrentApp.key;
    787         final UidDetail detail = mUidDetailProvider.getUidDetail(uid, true);
    788         mAppIcon.setImageDrawable(detail.icon);
    789 
    790         mAppTitles.removeAllViews();
    791         if (detail.detailLabels != null) {
    792             for (CharSequence label : detail.detailLabels) {
    793                 mAppTitles.addView(inflateAppTitle(inflater, mAppTitles, label));
    794             }
    795         } else {
    796             mAppTitles.addView(inflateAppTitle(inflater, mAppTitles, detail.label));
    797         }
    798 
    799         // enable settings button when package provides it
    800         final String[] packageNames = pm.getPackagesForUid(uid);
    801         if (packageNames != null && packageNames.length > 0) {
    802             mAppSettingsIntent = new Intent(Intent.ACTION_MANAGE_NETWORK_USAGE);
    803             mAppSettingsIntent.addCategory(Intent.CATEGORY_DEFAULT);
    804 
    805             // Search for match across all packages
    806             boolean matchFound = false;
    807             for (String packageName : packageNames) {
    808                 mAppSettingsIntent.setPackage(packageName);
    809                 if (pm.resolveActivity(mAppSettingsIntent, 0) != null) {
    810                     matchFound = true;
    811                     break;
    812                 }
    813             }
    814 
    815             mAppSettings.setEnabled(matchFound);
    816             mAppSettings.setVisibility(View.VISIBLE);
    817 
    818         } else {
    819             mAppSettingsIntent = null;
    820             mAppSettings.setVisibility(View.GONE);
    821         }
    822 
    823         updateDetailData();
    824 
    825         if (UserHandle.isApp(uid) && !mPolicyManager.getRestrictBackground()
    826                 && isBandwidthControlEnabled() && hasReadyMobileRadio(context)) {
    827             setPreferenceTitle(mAppRestrictView, R.string.data_usage_app_restrict_background);
    828             setPreferenceSummary(mAppRestrictView,
    829                     getString(R.string.data_usage_app_restrict_background_summary));
    830 
    831             mAppRestrictView.setVisibility(View.VISIBLE);
    832             mAppRestrict.setChecked(getAppRestrictBackground());
    833 
    834         } else {
    835             mAppRestrictView.setVisibility(View.GONE);
    836         }
    837     }
    838 
    839     private void setPolicyWarningBytes(long warningBytes) {
    840         if (LOGD) Log.d(TAG, "setPolicyWarningBytes()");
    841         mPolicyEditor.setPolicyWarningBytes(mTemplate, warningBytes);
    842         updatePolicy(false);
    843     }
    844 
    845     private void setPolicyLimitBytes(long limitBytes) {
    846         if (LOGD) Log.d(TAG, "setPolicyLimitBytes()");
    847         mPolicyEditor.setPolicyLimitBytes(mTemplate, limitBytes);
    848         updatePolicy(false);
    849     }
    850 
    851     /**
    852      * Local cache of value, used to work around delay when
    853      * {@link ConnectivityManager#setMobileDataEnabled(boolean)} is async.
    854      */
    855     private Boolean mMobileDataEnabled;
    856 
    857     private boolean isMobileDataEnabled() {
    858         if (mMobileDataEnabled != null) {
    859             // TODO: deprecate and remove this once enabled flag is on policy
    860             return mMobileDataEnabled;
    861         } else {
    862             return mConnService.getMobileDataEnabled();
    863         }
    864     }
    865 
    866     private void setMobileDataEnabled(boolean enabled) {
    867         if (LOGD) Log.d(TAG, "setMobileDataEnabled()");
    868         mConnService.setMobileDataEnabled(enabled);
    869         mMobileDataEnabled = enabled;
    870         updatePolicy(false);
    871     }
    872 
    873     private boolean isNetworkPolicyModifiable(NetworkPolicy policy) {
    874         return policy != null && isBandwidthControlEnabled() && mDataEnabled.isChecked()
    875                 && ActivityManager.getCurrentUser() == UserHandle.USER_OWNER;
    876     }
    877 
    878     private boolean isBandwidthControlEnabled() {
    879         try {
    880             return mNetworkService.isBandwidthControlEnabled();
    881         } catch (RemoteException e) {
    882             Log.w(TAG, "problem talking with INetworkManagementService: " + e);
    883             return false;
    884         }
    885     }
    886 
    887     private boolean getDataRoaming() {
    888         final ContentResolver resolver = getActivity().getContentResolver();
    889         return Settings.Global.getInt(resolver, Settings.Global.DATA_ROAMING, 0) != 0;
    890     }
    891 
    892     private void setDataRoaming(boolean enabled) {
    893         // TODO: teach telephony DataConnectionTracker to watch and apply
    894         // updates when changed.
    895         final ContentResolver resolver = getActivity().getContentResolver();
    896         Settings.Global.putInt(resolver, Settings.Global.DATA_ROAMING, enabled ? 1 : 0);
    897         mMenuDataRoaming.setChecked(enabled);
    898     }
    899 
    900     public void setRestrictBackground(boolean restrictBackground) {
    901         mPolicyManager.setRestrictBackground(restrictBackground);
    902         mMenuRestrictBackground.setChecked(restrictBackground);
    903     }
    904 
    905     private boolean getAppRestrictBackground() {
    906         final int uid = mCurrentApp.key;
    907         final int uidPolicy = mPolicyManager.getUidPolicy(uid);
    908         return (uidPolicy & POLICY_REJECT_METERED_BACKGROUND) != 0;
    909     }
    910 
    911     private void setAppRestrictBackground(boolean restrictBackground) {
    912         if (LOGD) Log.d(TAG, "setAppRestrictBackground()");
    913         final int uid = mCurrentApp.key;
    914         mPolicyManager.setUidPolicy(
    915                 uid, restrictBackground ? POLICY_REJECT_METERED_BACKGROUND : POLICY_NONE);
    916         mAppRestrict.setChecked(restrictBackground);
    917     }
    918 
    919     /**
    920      * Update chart sweeps and cycle list to reflect {@link NetworkPolicy} for
    921      * current {@link #mTemplate}.
    922      */
    923     private void updatePolicy(boolean refreshCycle) {
    924         if (isAppDetailMode()) {
    925             mNetworkSwitches.setVisibility(View.GONE);
    926         } else {
    927             mNetworkSwitches.setVisibility(View.VISIBLE);
    928         }
    929 
    930         // TODO: move enabled state directly into policy
    931         if (TAB_MOBILE.equals(mCurrentTab)) {
    932             mBinding = true;
    933             mDataEnabled.setChecked(isMobileDataEnabled());
    934             mBinding = false;
    935         }
    936 
    937         final NetworkPolicy policy = mPolicyEditor.getPolicy(mTemplate);
    938         if (isNetworkPolicyModifiable(policy)) {
    939             mDisableAtLimitView.setVisibility(View.VISIBLE);
    940             mDisableAtLimit.setChecked(policy != null && policy.limitBytes != LIMIT_DISABLED);
    941             if (!isAppDetailMode()) {
    942                 mChart.bindNetworkPolicy(policy);
    943             }
    944 
    945         } else {
    946             // controls are disabled; don't bind warning/limit sweeps
    947             mDisableAtLimitView.setVisibility(View.GONE);
    948             mChart.bindNetworkPolicy(null);
    949         }
    950 
    951         if (refreshCycle) {
    952             // generate cycle list based on policy and available history
    953             updateCycleList(policy);
    954         }
    955     }
    956 
    957     /**
    958      * Rebuild {@link #mCycleAdapter} based on {@link NetworkPolicy#cycleDay}
    959      * and available {@link NetworkStatsHistory} data. Always selects the newest
    960      * item, updating the inspection range on {@link #mChart}.
    961      */
    962     private void updateCycleList(NetworkPolicy policy) {
    963         // stash away currently selected cycle to try restoring below
    964         final CycleItem previousItem = (CycleItem) mCycleSpinner.getSelectedItem();
    965         mCycleAdapter.clear();
    966 
    967         final Context context = mCycleSpinner.getContext();
    968 
    969         long historyStart = Long.MAX_VALUE;
    970         long historyEnd = Long.MIN_VALUE;
    971         if (mChartData != null) {
    972             historyStart = mChartData.network.getStart();
    973             historyEnd = mChartData.network.getEnd();
    974         }
    975 
    976         final long now = System.currentTimeMillis();
    977         if (historyStart == Long.MAX_VALUE) historyStart = now;
    978         if (historyEnd == Long.MIN_VALUE) historyEnd = now + 1;
    979 
    980         boolean hasCycles = false;
    981         if (policy != null) {
    982             // find the next cycle boundary
    983             long cycleEnd = computeNextCycleBoundary(historyEnd, policy);
    984 
    985             // walk backwards, generating all valid cycle ranges
    986             while (cycleEnd > historyStart) {
    987                 final long cycleStart = computeLastCycleBoundary(cycleEnd, policy);
    988                 Log.d(TAG, "generating cs=" + cycleStart + " to ce=" + cycleEnd + " waiting for hs="
    989                         + historyStart);
    990                 mCycleAdapter.add(new CycleItem(context, cycleStart, cycleEnd));
    991                 cycleEnd = cycleStart;
    992                 hasCycles = true;
    993             }
    994 
    995             // one last cycle entry to modify policy cycle day
    996             mCycleAdapter.setChangePossible(isNetworkPolicyModifiable(policy));
    997         }
    998 
    999         if (!hasCycles) {
   1000             // no policy defined cycles; show entry for each four-week period
   1001             long cycleEnd = historyEnd;
   1002             while (cycleEnd > historyStart) {
   1003                 final long cycleStart = cycleEnd - (DateUtils.WEEK_IN_MILLIS * 4);
   1004                 mCycleAdapter.add(new CycleItem(context, cycleStart, cycleEnd));
   1005                 cycleEnd = cycleStart;
   1006             }
   1007 
   1008             mCycleAdapter.setChangePossible(false);
   1009         }
   1010 
   1011         // force pick the current cycle (first item)
   1012         if (mCycleAdapter.getCount() > 0) {
   1013             final int position = mCycleAdapter.findNearestPosition(previousItem);
   1014             mCycleSpinner.setSelection(position);
   1015 
   1016             // only force-update cycle when changed; skipping preserves any
   1017             // user-defined inspection region.
   1018             final CycleItem selectedItem = mCycleAdapter.getItem(position);
   1019             if (!Objects.equal(selectedItem, previousItem)) {
   1020                 mCycleListener.onItemSelected(mCycleSpinner, null, position, 0);
   1021             } else {
   1022                 // but still kick off loader for detailed list
   1023                 updateDetailData();
   1024             }
   1025         } else {
   1026             updateDetailData();
   1027         }
   1028     }
   1029 
   1030     private OnCheckedChangeListener mDataEnabledListener = new OnCheckedChangeListener() {
   1031         @Override
   1032         public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
   1033             if (mBinding) return;
   1034 
   1035             final boolean dataEnabled = isChecked;
   1036             final String currentTab = mCurrentTab;
   1037             if (TAB_MOBILE.equals(currentTab)) {
   1038                 if (dataEnabled) {
   1039                     setMobileDataEnabled(true);
   1040                 } else {
   1041                     // disabling data; show confirmation dialog which eventually
   1042                     // calls setMobileDataEnabled() once user confirms.
   1043                     ConfirmDataDisableFragment.show(DataUsageSummary.this);
   1044                 }
   1045             }
   1046 
   1047             updatePolicy(false);
   1048         }
   1049     };
   1050 
   1051     private View.OnClickListener mDisableAtLimitListener = new View.OnClickListener() {
   1052         @Override
   1053         public void onClick(View v) {
   1054             final boolean disableAtLimit = !mDisableAtLimit.isChecked();
   1055             if (disableAtLimit) {
   1056                 // enabling limit; show confirmation dialog which eventually
   1057                 // calls setPolicyLimitBytes() once user confirms.
   1058                 ConfirmLimitFragment.show(DataUsageSummary.this);
   1059             } else {
   1060                 setPolicyLimitBytes(LIMIT_DISABLED);
   1061             }
   1062         }
   1063     };
   1064 
   1065     private View.OnClickListener mAppRestrictListener = new View.OnClickListener() {
   1066         @Override
   1067         public void onClick(View v) {
   1068             final boolean restrictBackground = !mAppRestrict.isChecked();
   1069 
   1070             if (restrictBackground) {
   1071                 // enabling restriction; show confirmation dialog which
   1072                 // eventually calls setRestrictBackground() once user
   1073                 // confirms.
   1074                 ConfirmAppRestrictFragment.show(DataUsageSummary.this);
   1075             } else {
   1076                 setAppRestrictBackground(false);
   1077             }
   1078         }
   1079     };
   1080 
   1081     private OnClickListener mAppSettingsListener = new OnClickListener() {
   1082         @Override
   1083         public void onClick(View v) {
   1084             if (!isAdded()) return;
   1085 
   1086             // TODO: target torwards entire UID instead of just first package
   1087             startActivity(mAppSettingsIntent);
   1088         }
   1089     };
   1090 
   1091     private OnItemClickListener mListListener = new OnItemClickListener() {
   1092         @Override
   1093         public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
   1094             final Context context = view.getContext();
   1095             final AppItem app = (AppItem) parent.getItemAtPosition(position);
   1096 
   1097             // TODO: sigh, remove this hack once we understand 6450986
   1098             if (mUidDetailProvider == null || app == null) return;
   1099 
   1100             final UidDetail detail = mUidDetailProvider.getUidDetail(app.key, true);
   1101             AppDetailsFragment.show(DataUsageSummary.this, app, detail.label);
   1102         }
   1103     };
   1104 
   1105     private OnItemSelectedListener mCycleListener = new OnItemSelectedListener() {
   1106         @Override
   1107         public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
   1108             final CycleItem cycle = (CycleItem) parent.getItemAtPosition(position);
   1109             if (cycle instanceof CycleChangeItem) {
   1110                 // show cycle editor; will eventually call setPolicyCycleDay()
   1111                 // when user finishes editing.
   1112                 CycleEditorFragment.show(DataUsageSummary.this);
   1113 
   1114                 // reset spinner to something other than "change cycle..."
   1115                 mCycleSpinner.setSelection(0);
   1116 
   1117             } else {
   1118                 if (LOGD) {
   1119                     Log.d(TAG, "showing cycle " + cycle + ", start=" + cycle.start + ", end="
   1120                             + cycle.end + "]");
   1121                 }
   1122 
   1123                 // update chart to show selected cycle, and update detail data
   1124                 // to match updated sweep bounds.
   1125                 mChart.setVisibleRange(cycle.start, cycle.end);
   1126 
   1127                 updateDetailData();
   1128             }
   1129         }
   1130 
   1131         @Override
   1132         public void onNothingSelected(AdapterView<?> parent) {
   1133             // ignored
   1134         }
   1135     };
   1136 
   1137     /**
   1138      * Update details based on {@link #mChart} inspection range depending on
   1139      * current mode. In network mode, updates {@link #mAdapter} with sorted list
   1140      * of applications data usage, and when {@link #isAppDetailMode()} update
   1141      * app details.
   1142      */
   1143     private void updateDetailData() {
   1144         if (LOGD) Log.d(TAG, "updateDetailData()");
   1145 
   1146         final long start = mChart.getInspectStart();
   1147         final long end = mChart.getInspectEnd();
   1148         final long now = System.currentTimeMillis();
   1149 
   1150         final Context context = getActivity();
   1151 
   1152         NetworkStatsHistory.Entry entry = null;
   1153         if (isAppDetailMode() && mChartData != null && mChartData.detail != null) {
   1154             // bind foreground/background to piechart and labels
   1155             entry = mChartData.detailDefault.getValues(start, end, now, entry);
   1156             final long defaultBytes = entry.rxBytes + entry.txBytes;
   1157             entry = mChartData.detailForeground.getValues(start, end, now, entry);
   1158             final long foregroundBytes = entry.rxBytes + entry.txBytes;
   1159 
   1160             mAppPieChart.setOriginAngle(175);
   1161 
   1162             mAppPieChart.removeAllSlices();
   1163             mAppPieChart.addSlice(foregroundBytes, Color.parseColor("#d88d3a"));
   1164             mAppPieChart.addSlice(defaultBytes, Color.parseColor("#666666"));
   1165 
   1166             mAppPieChart.generatePath();
   1167 
   1168             mAppBackground.setText(Formatter.formatFileSize(context, defaultBytes));
   1169             mAppForeground.setText(Formatter.formatFileSize(context, foregroundBytes));
   1170 
   1171             // and finally leave with summary data for label below
   1172             entry = mChartData.detail.getValues(start, end, now, null);
   1173 
   1174             getLoaderManager().destroyLoader(LOADER_SUMMARY);
   1175 
   1176         } else {
   1177             if (mChartData != null) {
   1178                 entry = mChartData.network.getValues(start, end, now, null);
   1179             }
   1180 
   1181             // kick off loader for detailed stats
   1182             getLoaderManager().restartLoader(LOADER_SUMMARY,
   1183                     SummaryForAllUidLoader.buildArgs(mTemplate, start, end), mSummaryCallbacks);
   1184         }
   1185 
   1186         final long totalBytes = entry != null ? entry.rxBytes + entry.txBytes : 0;
   1187         final String totalPhrase = Formatter.formatFileSize(context, totalBytes);
   1188         final String rangePhrase = formatDateRange(context, start, end);
   1189 
   1190         final int summaryRes;
   1191         if (TAB_MOBILE.equals(mCurrentTab) || TAB_3G.equals(mCurrentApp)
   1192                 || TAB_4G.equals(mCurrentApp)) {
   1193             summaryRes = R.string.data_usage_total_during_range_mobile;
   1194         } else {
   1195             summaryRes = R.string.data_usage_total_during_range;
   1196         }
   1197 
   1198         mUsageSummary.setText(getString(summaryRes, totalPhrase, rangePhrase));
   1199 
   1200         // initial layout is finished above, ensure we have transitions
   1201         ensureLayoutTransitions();
   1202     }
   1203 
   1204     private final LoaderCallbacks<ChartData> mChartDataCallbacks = new LoaderCallbacks<
   1205             ChartData>() {
   1206         @Override
   1207         public Loader<ChartData> onCreateLoader(int id, Bundle args) {
   1208             return new ChartDataLoader(getActivity(), mStatsSession, args);
   1209         }
   1210 
   1211         @Override
   1212         public void onLoadFinished(Loader<ChartData> loader, ChartData data) {
   1213             mChartData = data;
   1214             mChart.bindNetworkStats(mChartData.network);
   1215             mChart.bindDetailNetworkStats(mChartData.detail);
   1216 
   1217             // calcuate policy cycles based on available data
   1218             updatePolicy(true);
   1219             updateAppDetail();
   1220 
   1221             // force scroll to top of body when showing detail
   1222             if (mChartData.detail != null) {
   1223                 mListView.smoothScrollToPosition(0);
   1224             }
   1225         }
   1226 
   1227         @Override
   1228         public void onLoaderReset(Loader<ChartData> loader) {
   1229             mChartData = null;
   1230             mChart.bindNetworkStats(null);
   1231             mChart.bindDetailNetworkStats(null);
   1232         }
   1233     };
   1234 
   1235     private final LoaderCallbacks<NetworkStats> mSummaryCallbacks = new LoaderCallbacks<
   1236             NetworkStats>() {
   1237         @Override
   1238         public Loader<NetworkStats> onCreateLoader(int id, Bundle args) {
   1239             return new SummaryForAllUidLoader(getActivity(), mStatsSession, args);
   1240         }
   1241 
   1242         @Override
   1243         public void onLoadFinished(Loader<NetworkStats> loader, NetworkStats data) {
   1244             final int[] restrictedUids = mPolicyManager.getUidsWithPolicy(
   1245                     POLICY_REJECT_METERED_BACKGROUND);
   1246             mAdapter.bindStats(data, restrictedUids);
   1247             updateEmptyVisible();
   1248         }
   1249 
   1250         @Override
   1251         public void onLoaderReset(Loader<NetworkStats> loader) {
   1252             mAdapter.bindStats(null, new int[0]);
   1253             updateEmptyVisible();
   1254         }
   1255 
   1256         private void updateEmptyVisible() {
   1257             final boolean isEmpty = mAdapter.isEmpty() && !isAppDetailMode();
   1258             mEmpty.setVisibility(isEmpty ? View.VISIBLE : View.GONE);
   1259         }
   1260     };
   1261 
   1262     @Deprecated
   1263     private boolean isMobilePolicySplit() {
   1264         final Context context = getActivity();
   1265         if (hasReadyMobileRadio(context)) {
   1266             final TelephonyManager tele = TelephonyManager.from(context);
   1267             return mPolicyEditor.isMobilePolicySplit(getActiveSubscriberId(context));
   1268         } else {
   1269             return false;
   1270         }
   1271     }
   1272 
   1273     @Deprecated
   1274     private void setMobilePolicySplit(boolean split) {
   1275         final Context context = getActivity();
   1276         if (hasReadyMobileRadio(context)) {
   1277             final TelephonyManager tele = TelephonyManager.from(context);
   1278             mPolicyEditor.setMobilePolicySplit(getActiveSubscriberId(context), split);
   1279         }
   1280     }
   1281 
   1282     private static String getActiveSubscriberId(Context context) {
   1283         final TelephonyManager tele = TelephonyManager.from(context);
   1284         final String actualSubscriberId = tele.getSubscriberId();
   1285         return SystemProperties.get(TEST_SUBSCRIBER_PROP, actualSubscriberId);
   1286     }
   1287 
   1288     private DataUsageChartListener mChartListener = new DataUsageChartListener() {
   1289         @Override
   1290         public void onInspectRangeChanged() {
   1291             if (LOGD) Log.d(TAG, "onInspectRangeChanged()");
   1292             updateDetailData();
   1293         }
   1294 
   1295         @Override
   1296         public void onWarningChanged() {
   1297             setPolicyWarningBytes(mChart.getWarningBytes());
   1298         }
   1299 
   1300         @Override
   1301         public void onLimitChanged() {
   1302             setPolicyLimitBytes(mChart.getLimitBytes());
   1303         }
   1304 
   1305         @Override
   1306         public void requestWarningEdit() {
   1307             WarningEditorFragment.show(DataUsageSummary.this);
   1308         }
   1309 
   1310         @Override
   1311         public void requestLimitEdit() {
   1312             LimitEditorFragment.show(DataUsageSummary.this);
   1313         }
   1314     };
   1315 
   1316     /**
   1317      * List item that reflects a specific data usage cycle.
   1318      */
   1319     public static class CycleItem implements Comparable<CycleItem> {
   1320         public CharSequence label;
   1321         public long start;
   1322         public long end;
   1323 
   1324         CycleItem(CharSequence label) {
   1325             this.label = label;
   1326         }
   1327 
   1328         public CycleItem(Context context, long start, long end) {
   1329             this.label = formatDateRange(context, start, end);
   1330             this.start = start;
   1331             this.end = end;
   1332         }
   1333 
   1334         @Override
   1335         public String toString() {
   1336             return label.toString();
   1337         }
   1338 
   1339         @Override
   1340         public boolean equals(Object o) {
   1341             if (o instanceof CycleItem) {
   1342                 final CycleItem another = (CycleItem) o;
   1343                 return start == another.start && end == another.end;
   1344             }
   1345             return false;
   1346         }
   1347 
   1348         @Override
   1349         public int compareTo(CycleItem another) {
   1350             return Long.compare(start, another.start);
   1351         }
   1352     }
   1353 
   1354     private static final StringBuilder sBuilder = new StringBuilder(50);
   1355     private static final java.util.Formatter sFormatter = new java.util.Formatter(
   1356             sBuilder, Locale.getDefault());
   1357 
   1358     public static String formatDateRange(Context context, long start, long end) {
   1359         final int flags = FORMAT_SHOW_DATE | FORMAT_ABBREV_MONTH;
   1360 
   1361         synchronized (sBuilder) {
   1362             sBuilder.setLength(0);
   1363             return DateUtils.formatDateRange(context, sFormatter, start, end, flags, null)
   1364                     .toString();
   1365         }
   1366     }
   1367 
   1368     /**
   1369      * Special-case data usage cycle that triggers dialog to change
   1370      * {@link NetworkPolicy#cycleDay}.
   1371      */
   1372     public static class CycleChangeItem extends CycleItem {
   1373         public CycleChangeItem(Context context) {
   1374             super(context.getString(R.string.data_usage_change_cycle));
   1375         }
   1376     }
   1377 
   1378     public static class CycleAdapter extends ArrayAdapter<CycleItem> {
   1379         private boolean mChangePossible = false;
   1380         private boolean mChangeVisible = false;
   1381 
   1382         private final CycleChangeItem mChangeItem;
   1383 
   1384         public CycleAdapter(Context context) {
   1385             super(context, android.R.layout.simple_spinner_item);
   1386             setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
   1387             mChangeItem = new CycleChangeItem(context);
   1388         }
   1389 
   1390         public void setChangePossible(boolean possible) {
   1391             mChangePossible = possible;
   1392             updateChange();
   1393         }
   1394 
   1395         public void setChangeVisible(boolean visible) {
   1396             mChangeVisible = visible;
   1397             updateChange();
   1398         }
   1399 
   1400         private void updateChange() {
   1401             remove(mChangeItem);
   1402             if (mChangePossible && mChangeVisible) {
   1403                 add(mChangeItem);
   1404             }
   1405         }
   1406 
   1407         /**
   1408          * Find position of {@link CycleItem} in this adapter which is nearest
   1409          * the given {@link CycleItem}.
   1410          */
   1411         public int findNearestPosition(CycleItem target) {
   1412             if (target != null) {
   1413                 final int count = getCount();
   1414                 for (int i = count - 1; i >= 0; i--) {
   1415                     final CycleItem item = getItem(i);
   1416                     if (item instanceof CycleChangeItem) {
   1417                         continue;
   1418                     } else if (item.compareTo(target) >= 0) {
   1419                         return i;
   1420                     }
   1421                 }
   1422             }
   1423             return 0;
   1424         }
   1425     }
   1426 
   1427     public static class AppItem implements Comparable<AppItem>, Parcelable {
   1428         public final int key;
   1429         public boolean restricted;
   1430         public SparseBooleanArray uids = new SparseBooleanArray();
   1431         public long total;
   1432 
   1433         public AppItem(int key) {
   1434             this.key = key;
   1435         }
   1436 
   1437         public AppItem(Parcel parcel) {
   1438             key = parcel.readInt();
   1439             uids = parcel.readSparseBooleanArray();
   1440             total = parcel.readLong();
   1441         }
   1442 
   1443         public void addUid(int uid) {
   1444             uids.put(uid, true);
   1445         }
   1446 
   1447         @Override
   1448         public void writeToParcel(Parcel dest, int flags) {
   1449             dest.writeInt(key);
   1450             dest.writeSparseBooleanArray(uids);
   1451             dest.writeLong(total);
   1452         }
   1453 
   1454         @Override
   1455         public int describeContents() {
   1456             return 0;
   1457         }
   1458 
   1459         @Override
   1460         public int compareTo(AppItem another) {
   1461             return Long.compare(another.total, total);
   1462         }
   1463 
   1464         public static final Creator<AppItem> CREATOR = new Creator<AppItem>() {
   1465             @Override
   1466             public AppItem createFromParcel(Parcel in) {
   1467                 return new AppItem(in);
   1468             }
   1469 
   1470             @Override
   1471             public AppItem[] newArray(int size) {
   1472                 return new AppItem[size];
   1473             }
   1474         };
   1475     }
   1476 
   1477     /**
   1478      * Adapter of applications, sorted by total usage descending.
   1479      */
   1480     public static class DataUsageAdapter extends BaseAdapter {
   1481         private final UidDetailProvider mProvider;
   1482         private final int mInsetSide;
   1483 
   1484         private ArrayList<AppItem> mItems = Lists.newArrayList();
   1485         private long mLargest;
   1486 
   1487         public DataUsageAdapter(UidDetailProvider provider, int insetSide) {
   1488             mProvider = checkNotNull(provider);
   1489             mInsetSide = insetSide;
   1490         }
   1491 
   1492         /**
   1493          * Bind the given {@link NetworkStats}, or {@code null} to clear list.
   1494          */
   1495         public void bindStats(NetworkStats stats, int[] restrictedUids) {
   1496             mItems.clear();
   1497 
   1498             final int currentUserId = ActivityManager.getCurrentUser();
   1499             final SparseArray<AppItem> knownItems = new SparseArray<AppItem>();
   1500 
   1501             NetworkStats.Entry entry = null;
   1502             final int size = stats != null ? stats.size() : 0;
   1503             for (int i = 0; i < size; i++) {
   1504                 entry = stats.getValues(i, entry);
   1505 
   1506                 // Decide how to collapse items together
   1507                 final int uid = entry.uid;
   1508                 final int collapseKey;
   1509                 if (UserHandle.isApp(uid)) {
   1510                     if (UserHandle.getUserId(uid) == currentUserId) {
   1511                         collapseKey = uid;
   1512                     } else {
   1513                         collapseKey = UidDetailProvider.buildKeyForUser(UserHandle.getUserId(uid));
   1514                     }
   1515                 } else if (uid == UID_REMOVED || uid == UID_TETHERING) {
   1516                     collapseKey = uid;
   1517                 } else {
   1518                     collapseKey = android.os.Process.SYSTEM_UID;
   1519                 }
   1520 
   1521                 AppItem item = knownItems.get(collapseKey);
   1522                 if (item == null) {
   1523                     item = new AppItem(collapseKey);
   1524                     mItems.add(item);
   1525                     knownItems.put(item.key, item);
   1526                 }
   1527                 item.addUid(uid);
   1528                 item.total += entry.rxBytes + entry.txBytes;
   1529             }
   1530 
   1531             for (int uid : restrictedUids) {
   1532                 // Only splice in restricted state for current user
   1533                 if (UserHandle.getUserId(uid) != currentUserId) continue;
   1534 
   1535                 AppItem item = knownItems.get(uid);
   1536                 if (item == null) {
   1537                     item = new AppItem(uid);
   1538                     item.total = -1;
   1539                     mItems.add(item);
   1540                     knownItems.put(item.key, item);
   1541                 }
   1542                 item.restricted = true;
   1543             }
   1544 
   1545             Collections.sort(mItems);
   1546             mLargest = (mItems.size() > 0) ? mItems.get(0).total : 0;
   1547             notifyDataSetChanged();
   1548         }
   1549 
   1550         @Override
   1551         public int getCount() {
   1552             return mItems.size();
   1553         }
   1554 
   1555         @Override
   1556         public Object getItem(int position) {
   1557             return mItems.get(position);
   1558         }
   1559 
   1560         @Override
   1561         public long getItemId(int position) {
   1562             return mItems.get(position).key;
   1563         }
   1564 
   1565         @Override
   1566         public View getView(int position, View convertView, ViewGroup parent) {
   1567             if (convertView == null) {
   1568                 convertView = LayoutInflater.from(parent.getContext()).inflate(
   1569                         R.layout.data_usage_item, parent, false);
   1570 
   1571                 if (mInsetSide > 0) {
   1572                     convertView.setPaddingRelative(mInsetSide, 0, mInsetSide, 0);
   1573                 }
   1574             }
   1575 
   1576             final Context context = parent.getContext();
   1577 
   1578             final TextView text1 = (TextView) convertView.findViewById(android.R.id.text1);
   1579             final ProgressBar progress = (ProgressBar) convertView.findViewById(
   1580                     android.R.id.progress);
   1581 
   1582             // kick off async load of app details
   1583             final AppItem item = mItems.get(position);
   1584             UidDetailTask.bindView(mProvider, item, convertView);
   1585 
   1586             if (item.restricted && item.total <= 0) {
   1587                 text1.setText(R.string.data_usage_app_restricted);
   1588                 progress.setVisibility(View.GONE);
   1589             } else {
   1590                 text1.setText(Formatter.formatFileSize(context, item.total));
   1591                 progress.setVisibility(View.VISIBLE);
   1592             }
   1593 
   1594             final int percentTotal = mLargest != 0 ? (int) (item.total * 100 / mLargest) : 0;
   1595             progress.setProgress(percentTotal);
   1596 
   1597             return convertView;
   1598         }
   1599     }
   1600 
   1601     /**
   1602      * Empty {@link Fragment} that controls display of UID details in
   1603      * {@link DataUsageSummary}.
   1604      */
   1605     public static class AppDetailsFragment extends Fragment {
   1606         private static final String EXTRA_APP = "app";
   1607 
   1608         public static void show(DataUsageSummary parent, AppItem app, CharSequence label) {
   1609             if (!parent.isAdded()) return;
   1610 
   1611             final Bundle args = new Bundle();
   1612             args.putParcelable(EXTRA_APP, app);
   1613 
   1614             final AppDetailsFragment fragment = new AppDetailsFragment();
   1615             fragment.setArguments(args);
   1616             fragment.setTargetFragment(parent, 0);
   1617             final FragmentTransaction ft = parent.getFragmentManager().beginTransaction();
   1618             ft.add(fragment, TAG_APP_DETAILS);
   1619             ft.addToBackStack(TAG_APP_DETAILS);
   1620             ft.setBreadCrumbTitle(label);
   1621             ft.commitAllowingStateLoss();
   1622         }
   1623 
   1624         @Override
   1625         public void onStart() {
   1626             super.onStart();
   1627             final DataUsageSummary target = (DataUsageSummary) getTargetFragment();
   1628             target.mCurrentApp = getArguments().getParcelable(EXTRA_APP);
   1629             target.updateBody();
   1630         }
   1631 
   1632         @Override
   1633         public void onStop() {
   1634             super.onStop();
   1635             final DataUsageSummary target = (DataUsageSummary) getTargetFragment();
   1636             target.mCurrentApp = null;
   1637             target.updateBody();
   1638         }
   1639     }
   1640 
   1641     /**
   1642      * Dialog to request user confirmation before setting
   1643      * {@link NetworkPolicy#limitBytes}.
   1644      */
   1645     public static class ConfirmLimitFragment extends DialogFragment {
   1646         private static final String EXTRA_MESSAGE = "message";
   1647         private static final String EXTRA_LIMIT_BYTES = "limitBytes";
   1648 
   1649         public static void show(DataUsageSummary parent) {
   1650             if (!parent.isAdded()) return;
   1651 
   1652             final Resources res = parent.getResources();
   1653             final CharSequence message;
   1654             final long minLimitBytes = (long) (
   1655                     parent.mPolicyEditor.getPolicy(parent.mTemplate).warningBytes * 1.2f);
   1656             final long limitBytes;
   1657 
   1658             // TODO: customize default limits based on network template
   1659             final String currentTab = parent.mCurrentTab;
   1660             if (TAB_3G.equals(currentTab)) {
   1661                 message = res.getString(R.string.data_usage_limit_dialog_mobile);
   1662                 limitBytes = Math.max(5 * GB_IN_BYTES, minLimitBytes);
   1663             } else if (TAB_4G.equals(currentTab)) {
   1664                 message = res.getString(R.string.data_usage_limit_dialog_mobile);
   1665                 limitBytes = Math.max(5 * GB_IN_BYTES, minLimitBytes);
   1666             } else if (TAB_MOBILE.equals(currentTab)) {
   1667                 message = res.getString(R.string.data_usage_limit_dialog_mobile);
   1668                 limitBytes = Math.max(5 * GB_IN_BYTES, minLimitBytes);
   1669             } else {
   1670                 throw new IllegalArgumentException("unknown current tab: " + currentTab);
   1671             }
   1672 
   1673             final Bundle args = new Bundle();
   1674             args.putCharSequence(EXTRA_MESSAGE, message);
   1675             args.putLong(EXTRA_LIMIT_BYTES, limitBytes);
   1676 
   1677             final ConfirmLimitFragment dialog = new ConfirmLimitFragment();
   1678             dialog.setArguments(args);
   1679             dialog.setTargetFragment(parent, 0);
   1680             dialog.show(parent.getFragmentManager(), TAG_CONFIRM_LIMIT);
   1681         }
   1682 
   1683         @Override
   1684         public Dialog onCreateDialog(Bundle savedInstanceState) {
   1685             final Context context = getActivity();
   1686 
   1687             final CharSequence message = getArguments().getCharSequence(EXTRA_MESSAGE);
   1688             final long limitBytes = getArguments().getLong(EXTRA_LIMIT_BYTES);
   1689 
   1690             final AlertDialog.Builder builder = new AlertDialog.Builder(context);
   1691             builder.setTitle(R.string.data_usage_limit_dialog_title);
   1692             builder.setMessage(message);
   1693 
   1694             builder.setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() {
   1695                 @Override
   1696                 public void onClick(DialogInterface dialog, int which) {
   1697                     final DataUsageSummary target = (DataUsageSummary) getTargetFragment();
   1698                     if (target != null) {
   1699                         target.setPolicyLimitBytes(limitBytes);
   1700                     }
   1701                 }
   1702             });
   1703 
   1704             return builder.create();
   1705         }
   1706     }
   1707 
   1708     /**
   1709      * Dialog to edit {@link NetworkPolicy#cycleDay}.
   1710      */
   1711     public static class CycleEditorFragment extends DialogFragment {
   1712         private static final String EXTRA_TEMPLATE = "template";
   1713 
   1714         public static void show(DataUsageSummary parent) {
   1715             if (!parent.isAdded()) return;
   1716 
   1717             final Bundle args = new Bundle();
   1718             args.putParcelable(EXTRA_TEMPLATE, parent.mTemplate);
   1719 
   1720             final CycleEditorFragment dialog = new CycleEditorFragment();
   1721             dialog.setArguments(args);
   1722             dialog.setTargetFragment(parent, 0);
   1723             dialog.show(parent.getFragmentManager(), TAG_CYCLE_EDITOR);
   1724         }
   1725 
   1726         @Override
   1727         public Dialog onCreateDialog(Bundle savedInstanceState) {
   1728             final Context context = getActivity();
   1729             final DataUsageSummary target = (DataUsageSummary) getTargetFragment();
   1730             final NetworkPolicyEditor editor = target.mPolicyEditor;
   1731 
   1732             final AlertDialog.Builder builder = new AlertDialog.Builder(context);
   1733             final LayoutInflater dialogInflater = LayoutInflater.from(builder.getContext());
   1734 
   1735             final View view = dialogInflater.inflate(R.layout.data_usage_cycle_editor, null, false);
   1736             final NumberPicker cycleDayPicker = (NumberPicker) view.findViewById(R.id.cycle_day);
   1737 
   1738             final NetworkTemplate template = getArguments().getParcelable(EXTRA_TEMPLATE);
   1739             final int cycleDay = editor.getPolicyCycleDay(template);
   1740 
   1741             cycleDayPicker.setMinValue(1);
   1742             cycleDayPicker.setMaxValue(31);
   1743             cycleDayPicker.setValue(cycleDay);
   1744             cycleDayPicker.setWrapSelectorWheel(true);
   1745 
   1746             builder.setTitle(R.string.data_usage_cycle_editor_title);
   1747             builder.setView(view);
   1748 
   1749             builder.setPositiveButton(R.string.data_usage_cycle_editor_positive,
   1750                     new DialogInterface.OnClickListener() {
   1751                         @Override
   1752                         public void onClick(DialogInterface dialog, int which) {
   1753                             // clear focus to finish pending text edits
   1754                             cycleDayPicker.clearFocus();
   1755 
   1756                             final int cycleDay = cycleDayPicker.getValue();
   1757                             final String cycleTimezone = new Time().timezone;
   1758                             editor.setPolicyCycleDay(template, cycleDay, cycleTimezone);
   1759                             target.updatePolicy(true);
   1760                         }
   1761                     });
   1762 
   1763             return builder.create();
   1764         }
   1765     }
   1766 
   1767     /**
   1768      * Dialog to edit {@link NetworkPolicy#warningBytes}.
   1769      */
   1770     public static class WarningEditorFragment extends DialogFragment {
   1771         private static final String EXTRA_TEMPLATE = "template";
   1772 
   1773         public static void show(DataUsageSummary parent) {
   1774             if (!parent.isAdded()) return;
   1775 
   1776             final Bundle args = new Bundle();
   1777             args.putParcelable(EXTRA_TEMPLATE, parent.mTemplate);
   1778 
   1779             final WarningEditorFragment dialog = new WarningEditorFragment();
   1780             dialog.setArguments(args);
   1781             dialog.setTargetFragment(parent, 0);
   1782             dialog.show(parent.getFragmentManager(), TAG_WARNING_EDITOR);
   1783         }
   1784 
   1785         @Override
   1786         public Dialog onCreateDialog(Bundle savedInstanceState) {
   1787             final Context context = getActivity();
   1788             final DataUsageSummary target = (DataUsageSummary) getTargetFragment();
   1789             final NetworkPolicyEditor editor = target.mPolicyEditor;
   1790 
   1791             final AlertDialog.Builder builder = new AlertDialog.Builder(context);
   1792             final LayoutInflater dialogInflater = LayoutInflater.from(builder.getContext());
   1793 
   1794             final View view = dialogInflater.inflate(R.layout.data_usage_bytes_editor, null, false);
   1795             final NumberPicker bytesPicker = (NumberPicker) view.findViewById(R.id.bytes);
   1796 
   1797             final NetworkTemplate template = getArguments().getParcelable(EXTRA_TEMPLATE);
   1798             final long warningBytes = editor.getPolicyWarningBytes(template);
   1799             final long limitBytes = editor.getPolicyLimitBytes(template);
   1800 
   1801             bytesPicker.setMinValue(0);
   1802             if (limitBytes != LIMIT_DISABLED) {
   1803                 bytesPicker.setMaxValue((int) (limitBytes / MB_IN_BYTES) - 1);
   1804             } else {
   1805                 bytesPicker.setMaxValue(Integer.MAX_VALUE);
   1806             }
   1807             bytesPicker.setValue((int) (warningBytes / MB_IN_BYTES));
   1808             bytesPicker.setWrapSelectorWheel(false);
   1809 
   1810             builder.setTitle(R.string.data_usage_warning_editor_title);
   1811             builder.setView(view);
   1812 
   1813             builder.setPositiveButton(R.string.data_usage_cycle_editor_positive,
   1814                     new DialogInterface.OnClickListener() {
   1815                         @Override
   1816                         public void onClick(DialogInterface dialog, int which) {
   1817                             // clear focus to finish pending text edits
   1818                             bytesPicker.clearFocus();
   1819 
   1820                             final long bytes = bytesPicker.getValue() * MB_IN_BYTES;
   1821                             editor.setPolicyWarningBytes(template, bytes);
   1822                             target.updatePolicy(false);
   1823                         }
   1824                     });
   1825 
   1826             return builder.create();
   1827         }
   1828     }
   1829 
   1830     /**
   1831      * Dialog to edit {@link NetworkPolicy#limitBytes}.
   1832      */
   1833     public static class LimitEditorFragment extends DialogFragment {
   1834         private static final String EXTRA_TEMPLATE = "template";
   1835 
   1836         public static void show(DataUsageSummary parent) {
   1837             if (!parent.isAdded()) return;
   1838 
   1839             final Bundle args = new Bundle();
   1840             args.putParcelable(EXTRA_TEMPLATE, parent.mTemplate);
   1841 
   1842             final LimitEditorFragment dialog = new LimitEditorFragment();
   1843             dialog.setArguments(args);
   1844             dialog.setTargetFragment(parent, 0);
   1845             dialog.show(parent.getFragmentManager(), TAG_LIMIT_EDITOR);
   1846         }
   1847 
   1848         @Override
   1849         public Dialog onCreateDialog(Bundle savedInstanceState) {
   1850             final Context context = getActivity();
   1851             final DataUsageSummary target = (DataUsageSummary) getTargetFragment();
   1852             final NetworkPolicyEditor editor = target.mPolicyEditor;
   1853 
   1854             final AlertDialog.Builder builder = new AlertDialog.Builder(context);
   1855             final LayoutInflater dialogInflater = LayoutInflater.from(builder.getContext());
   1856 
   1857             final View view = dialogInflater.inflate(R.layout.data_usage_bytes_editor, null, false);
   1858             final NumberPicker bytesPicker = (NumberPicker) view.findViewById(R.id.bytes);
   1859 
   1860             final NetworkTemplate template = getArguments().getParcelable(EXTRA_TEMPLATE);
   1861             final long warningBytes = editor.getPolicyWarningBytes(template);
   1862             final long limitBytes = editor.getPolicyLimitBytes(template);
   1863 
   1864             bytesPicker.setMaxValue(Integer.MAX_VALUE);
   1865             if (warningBytes != WARNING_DISABLED && limitBytes > 0) {
   1866                 bytesPicker.setMinValue((int) (warningBytes / MB_IN_BYTES) + 1);
   1867             } else {
   1868                 bytesPicker.setMinValue(0);
   1869             }
   1870             bytesPicker.setValue((int) (limitBytes / MB_IN_BYTES));
   1871             bytesPicker.setWrapSelectorWheel(false);
   1872 
   1873             builder.setTitle(R.string.data_usage_limit_editor_title);
   1874             builder.setView(view);
   1875 
   1876             builder.setPositiveButton(R.string.data_usage_cycle_editor_positive,
   1877                     new DialogInterface.OnClickListener() {
   1878                         @Override
   1879                         public void onClick(DialogInterface dialog, int which) {
   1880                             // clear focus to finish pending text edits
   1881                             bytesPicker.clearFocus();
   1882 
   1883                             final long bytes = bytesPicker.getValue() * MB_IN_BYTES;
   1884                             editor.setPolicyLimitBytes(template, bytes);
   1885                             target.updatePolicy(false);
   1886                         }
   1887                     });
   1888 
   1889             return builder.create();
   1890         }
   1891     }
   1892     /**
   1893      * Dialog to request user confirmation before disabling data.
   1894      */
   1895     public static class ConfirmDataDisableFragment extends DialogFragment {
   1896         public static void show(DataUsageSummary parent) {
   1897             if (!parent.isAdded()) return;
   1898 
   1899             final ConfirmDataDisableFragment dialog = new ConfirmDataDisableFragment();
   1900             dialog.setTargetFragment(parent, 0);
   1901             dialog.show(parent.getFragmentManager(), TAG_CONFIRM_DATA_DISABLE);
   1902         }
   1903 
   1904         @Override
   1905         public Dialog onCreateDialog(Bundle savedInstanceState) {
   1906             final Context context = getActivity();
   1907 
   1908             final AlertDialog.Builder builder = new AlertDialog.Builder(context);
   1909             builder.setMessage(R.string.data_usage_disable_mobile);
   1910 
   1911             builder.setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() {
   1912                 @Override
   1913                 public void onClick(DialogInterface dialog, int which) {
   1914                     final DataUsageSummary target = (DataUsageSummary) getTargetFragment();
   1915                     if (target != null) {
   1916                         // TODO: extend to modify policy enabled flag.
   1917                         target.setMobileDataEnabled(false);
   1918                     }
   1919                 }
   1920             });
   1921             builder.setNegativeButton(android.R.string.cancel, null);
   1922 
   1923             return builder.create();
   1924         }
   1925     }
   1926 
   1927     /**
   1928      * Dialog to request user confirmation before setting
   1929      * {@link android.provider.Settings.Global#DATA_ROAMING}.
   1930      */
   1931     public static class ConfirmDataRoamingFragment extends DialogFragment {
   1932         public static void show(DataUsageSummary parent) {
   1933             if (!parent.isAdded()) return;
   1934 
   1935             final ConfirmDataRoamingFragment dialog = new ConfirmDataRoamingFragment();
   1936             dialog.setTargetFragment(parent, 0);
   1937             dialog.show(parent.getFragmentManager(), TAG_CONFIRM_DATA_ROAMING);
   1938         }
   1939 
   1940         @Override
   1941         public Dialog onCreateDialog(Bundle savedInstanceState) {
   1942             final Context context = getActivity();
   1943 
   1944             final AlertDialog.Builder builder = new AlertDialog.Builder(context);
   1945             builder.setTitle(R.string.roaming_reenable_title);
   1946             if (Utils.hasMultipleUsers(context)) {
   1947                 builder.setMessage(R.string.roaming_warning_multiuser);
   1948             } else {
   1949                 builder.setMessage(R.string.roaming_warning);
   1950             }
   1951 
   1952             builder.setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() {
   1953                 @Override
   1954                 public void onClick(DialogInterface dialog, int which) {
   1955                     final DataUsageSummary target = (DataUsageSummary) getTargetFragment();
   1956                     if (target != null) {
   1957                         target.setDataRoaming(true);
   1958                     }
   1959                 }
   1960             });
   1961             builder.setNegativeButton(android.R.string.cancel, null);
   1962 
   1963             return builder.create();
   1964         }
   1965     }
   1966 
   1967     /**
   1968      * Dialog to request user confirmation before setting
   1969      * {@link INetworkPolicyManager#setRestrictBackground(boolean)}.
   1970      */
   1971     public static class ConfirmRestrictFragment extends DialogFragment {
   1972         public static void show(DataUsageSummary parent) {
   1973             if (!parent.isAdded()) return;
   1974 
   1975             final ConfirmRestrictFragment dialog = new ConfirmRestrictFragment();
   1976             dialog.setTargetFragment(parent, 0);
   1977             dialog.show(parent.getFragmentManager(), TAG_CONFIRM_RESTRICT);
   1978         }
   1979 
   1980         @Override
   1981         public Dialog onCreateDialog(Bundle savedInstanceState) {
   1982             final Context context = getActivity();
   1983 
   1984             final AlertDialog.Builder builder = new AlertDialog.Builder(context);
   1985             builder.setTitle(R.string.data_usage_restrict_background_title);
   1986             if (Utils.hasMultipleUsers(context)) {
   1987                 builder.setMessage(R.string.data_usage_restrict_background_multiuser);
   1988             } else {
   1989                 builder.setMessage(R.string.data_usage_restrict_background);
   1990             }
   1991 
   1992             builder.setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() {
   1993                 @Override
   1994                 public void onClick(DialogInterface dialog, int which) {
   1995                     final DataUsageSummary target = (DataUsageSummary) getTargetFragment();
   1996                     if (target != null) {
   1997                         target.setRestrictBackground(true);
   1998                     }
   1999                 }
   2000             });
   2001             builder.setNegativeButton(android.R.string.cancel, null);
   2002 
   2003             return builder.create();
   2004         }
   2005     }
   2006 
   2007     /**
   2008      * Dialog to inform user that {@link #POLICY_REJECT_METERED_BACKGROUND}
   2009      * change has been denied, usually based on
   2010      * {@link DataUsageSummary#hasLimitedNetworks()}.
   2011      */
   2012     public static class DeniedRestrictFragment extends DialogFragment {
   2013         public static void show(DataUsageSummary parent) {
   2014             if (!parent.isAdded()) return;
   2015 
   2016             final DeniedRestrictFragment dialog = new DeniedRestrictFragment();
   2017             dialog.setTargetFragment(parent, 0);
   2018             dialog.show(parent.getFragmentManager(), TAG_DENIED_RESTRICT);
   2019         }
   2020 
   2021         @Override
   2022         public Dialog onCreateDialog(Bundle savedInstanceState) {
   2023             final Context context = getActivity();
   2024 
   2025             final AlertDialog.Builder builder = new AlertDialog.Builder(context);
   2026             builder.setTitle(R.string.data_usage_app_restrict_background);
   2027             builder.setMessage(R.string.data_usage_restrict_denied_dialog);
   2028             builder.setPositiveButton(android.R.string.ok, null);
   2029 
   2030             return builder.create();
   2031         }
   2032     }
   2033 
   2034     /**
   2035      * Dialog to request user confirmation before setting
   2036      * {@link #POLICY_REJECT_METERED_BACKGROUND}.
   2037      */
   2038     public static class ConfirmAppRestrictFragment extends DialogFragment {
   2039         public static void show(DataUsageSummary parent) {
   2040             if (!parent.isAdded()) return;
   2041 
   2042             final ConfirmAppRestrictFragment dialog = new ConfirmAppRestrictFragment();
   2043             dialog.setTargetFragment(parent, 0);
   2044             dialog.show(parent.getFragmentManager(), TAG_CONFIRM_APP_RESTRICT);
   2045         }
   2046 
   2047         @Override
   2048         public Dialog onCreateDialog(Bundle savedInstanceState) {
   2049             final Context context = getActivity();
   2050 
   2051             final AlertDialog.Builder builder = new AlertDialog.Builder(context);
   2052             builder.setTitle(R.string.data_usage_app_restrict_dialog_title);
   2053             builder.setMessage(R.string.data_usage_app_restrict_dialog);
   2054 
   2055             builder.setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() {
   2056                 @Override
   2057                 public void onClick(DialogInterface dialog, int which) {
   2058                     final DataUsageSummary target = (DataUsageSummary) getTargetFragment();
   2059                     if (target != null) {
   2060                         target.setAppRestrictBackground(true);
   2061                     }
   2062                 }
   2063             });
   2064             builder.setNegativeButton(android.R.string.cancel, null);
   2065 
   2066             return builder.create();
   2067         }
   2068     }
   2069 
   2070     /**
   2071      * Dialog to inform user about changing auto-sync setting
   2072      */
   2073     public static class ConfirmAutoSyncChangeFragment extends DialogFragment {
   2074         private static final String SAVE_ENABLING = "enabling";
   2075         private boolean mEnabling;
   2076 
   2077         public static void show(DataUsageSummary parent, boolean enabling) {
   2078             if (!parent.isAdded()) return;
   2079 
   2080             final ConfirmAutoSyncChangeFragment dialog = new ConfirmAutoSyncChangeFragment();
   2081             dialog.mEnabling = enabling;
   2082             dialog.setTargetFragment(parent, 0);
   2083             dialog.show(parent.getFragmentManager(), TAG_CONFIRM_AUTO_SYNC_CHANGE);
   2084         }
   2085 
   2086         @Override
   2087         public Dialog onCreateDialog(Bundle savedInstanceState) {
   2088             final Context context = getActivity();
   2089             if (savedInstanceState != null) {
   2090                 mEnabling = savedInstanceState.getBoolean(SAVE_ENABLING);
   2091             }
   2092 
   2093             final AlertDialog.Builder builder = new AlertDialog.Builder(context);
   2094             if (!mEnabling) {
   2095                 builder.setTitle(R.string.data_usage_auto_sync_off_dialog_title);
   2096                 builder.setMessage(R.string.data_usage_auto_sync_off_dialog);
   2097             } else {
   2098                 builder.setTitle(R.string.data_usage_auto_sync_on_dialog_title);
   2099                 builder.setMessage(R.string.data_usage_auto_sync_on_dialog);
   2100             }
   2101 
   2102             builder.setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() {
   2103                 @Override
   2104                 public void onClick(DialogInterface dialog, int which) {
   2105                     ContentResolver.setMasterSyncAutomatically(mEnabling);
   2106                 }
   2107             });
   2108             builder.setNegativeButton(android.R.string.cancel, null);
   2109 
   2110             return builder.create();
   2111         }
   2112 
   2113         @Override
   2114         public void onSaveInstanceState(Bundle outState) {
   2115             super.onSaveInstanceState(outState);
   2116             outState.putBoolean(SAVE_ENABLING, mEnabling);
   2117         }
   2118     }
   2119 
   2120     /**
   2121      * Compute default tab that should be selected, based on
   2122      * {@link NetworkPolicyManager#EXTRA_NETWORK_TEMPLATE} extra.
   2123      */
   2124     private static String computeTabFromIntent(Intent intent) {
   2125         final NetworkTemplate template = intent.getParcelableExtra(EXTRA_NETWORK_TEMPLATE);
   2126         if (template == null) return null;
   2127 
   2128         switch (template.getMatchRule()) {
   2129             case MATCH_MOBILE_3G_LOWER:
   2130                 return TAB_3G;
   2131             case MATCH_MOBILE_4G:
   2132                 return TAB_4G;
   2133             case MATCH_MOBILE_ALL:
   2134                 return TAB_MOBILE;
   2135             case MATCH_WIFI:
   2136                 return TAB_WIFI;
   2137             default:
   2138                 return null;
   2139         }
   2140     }
   2141 
   2142     /**
   2143      * Background task that loads {@link UidDetail}, binding to
   2144      * {@link DataUsageAdapter} row item when finished.
   2145      */
   2146     private static class UidDetailTask extends AsyncTask<Void, Void, UidDetail> {
   2147         private final UidDetailProvider mProvider;
   2148         private final AppItem mItem;
   2149         private final View mTarget;
   2150 
   2151         private UidDetailTask(UidDetailProvider provider, AppItem item, View target) {
   2152             mProvider = checkNotNull(provider);
   2153             mItem = checkNotNull(item);
   2154             mTarget = checkNotNull(target);
   2155         }
   2156 
   2157         public static void bindView(
   2158                 UidDetailProvider provider, AppItem item, View target) {
   2159             final UidDetailTask existing = (UidDetailTask) target.getTag();
   2160             if (existing != null) {
   2161                 existing.cancel(false);
   2162             }
   2163 
   2164             final UidDetail cachedDetail = provider.getUidDetail(item.key, false);
   2165             if (cachedDetail != null) {
   2166                 bindView(cachedDetail, target);
   2167             } else {
   2168                 target.setTag(new UidDetailTask(provider, item, target).executeOnExecutor(
   2169                         AsyncTask.THREAD_POOL_EXECUTOR));
   2170             }
   2171         }
   2172 
   2173         private static void bindView(UidDetail detail, View target) {
   2174             final ImageView icon = (ImageView) target.findViewById(android.R.id.icon);
   2175             final TextView title = (TextView) target.findViewById(android.R.id.title);
   2176 
   2177             if (detail != null) {
   2178                 icon.setImageDrawable(detail.icon);
   2179                 title.setText(detail.label);
   2180             } else {
   2181                 icon.setImageDrawable(null);
   2182                 title.setText(null);
   2183             }
   2184         }
   2185 
   2186         @Override
   2187         protected void onPreExecute() {
   2188             bindView(null, mTarget);
   2189         }
   2190 
   2191         @Override
   2192         protected UidDetail doInBackground(Void... params) {
   2193             return mProvider.getUidDetail(mItem.key, true);
   2194         }
   2195 
   2196         @Override
   2197         protected void onPostExecute(UidDetail result) {
   2198             bindView(result, mTarget);
   2199         }
   2200     }
   2201 
   2202     /**
   2203      * Test if device has a mobile data radio with SIM in ready state.
   2204      */
   2205     public static boolean hasReadyMobileRadio(Context context) {
   2206         if (TEST_RADIOS) {
   2207             return SystemProperties.get(TEST_RADIOS_PROP).contains("mobile");
   2208         }
   2209 
   2210         final ConnectivityManager conn = ConnectivityManager.from(context);
   2211         final TelephonyManager tele = TelephonyManager.from(context);
   2212 
   2213         // require both supported network and ready SIM
   2214         return conn.isNetworkSupported(TYPE_MOBILE) && tele.getSimState() == SIM_STATE_READY;
   2215     }
   2216 
   2217     /**
   2218      * Test if device has a mobile 4G data radio.
   2219      */
   2220     public static boolean hasReadyMobile4gRadio(Context context) {
   2221         if (!NetworkPolicyEditor.ENABLE_SPLIT_POLICIES) {
   2222             return false;
   2223         }
   2224         if (TEST_RADIOS) {
   2225             return SystemProperties.get(TEST_RADIOS_PROP).contains("4g");
   2226         }
   2227 
   2228         final ConnectivityManager conn = ConnectivityManager.from(context);
   2229         final TelephonyManager tele = TelephonyManager.from(context);
   2230 
   2231         final boolean hasWimax = conn.isNetworkSupported(TYPE_WIMAX);
   2232         final boolean hasLte = (tele.getLteOnCdmaMode() == PhoneConstants.LTE_ON_CDMA_TRUE)
   2233                 && hasReadyMobileRadio(context);
   2234         return hasWimax || hasLte;
   2235     }
   2236 
   2237     /**
   2238      * Test if device has a Wi-Fi data radio.
   2239      */
   2240     public static boolean hasWifiRadio(Context context) {
   2241         if (TEST_RADIOS) {
   2242             return SystemProperties.get(TEST_RADIOS_PROP).contains("wifi");
   2243         }
   2244 
   2245         final ConnectivityManager conn = ConnectivityManager.from(context);
   2246         return conn.isNetworkSupported(TYPE_WIFI);
   2247     }
   2248 
   2249     /**
   2250      * Test if device has an ethernet network connection.
   2251      */
   2252     public boolean hasEthernet(Context context) {
   2253         if (TEST_RADIOS) {
   2254             return SystemProperties.get(TEST_RADIOS_PROP).contains("ethernet");
   2255         }
   2256 
   2257         final ConnectivityManager conn = ConnectivityManager.from(context);
   2258         final boolean hasEthernet = conn.isNetworkSupported(TYPE_ETHERNET);
   2259 
   2260         final long ethernetBytes;
   2261         if (mStatsSession != null) {
   2262             try {
   2263                 ethernetBytes = mStatsSession.getSummaryForNetwork(
   2264                         NetworkTemplate.buildTemplateEthernet(), Long.MIN_VALUE, Long.MAX_VALUE)
   2265                         .getTotalBytes();
   2266             } catch (RemoteException e) {
   2267                 throw new RuntimeException(e);
   2268             }
   2269         } else {
   2270             ethernetBytes = 0;
   2271         }
   2272 
   2273         // only show ethernet when both hardware present and traffic has occurred
   2274         return hasEthernet && ethernetBytes > 0;
   2275     }
   2276 
   2277     /**
   2278      * Inflate a {@link Preference} style layout, adding the given {@link View}
   2279      * widget into {@link android.R.id#widget_frame}.
   2280      */
   2281     private static View inflatePreference(LayoutInflater inflater, ViewGroup root, View widget) {
   2282         final View view = inflater.inflate(R.layout.preference, root, false);
   2283         final LinearLayout widgetFrame = (LinearLayout) view.findViewById(
   2284                 android.R.id.widget_frame);
   2285         widgetFrame.addView(widget, new LinearLayout.LayoutParams(WRAP_CONTENT, WRAP_CONTENT));
   2286         return view;
   2287     }
   2288 
   2289     private static View inflateAppTitle(
   2290             LayoutInflater inflater, ViewGroup root, CharSequence label) {
   2291         final TextView view = (TextView) inflater.inflate(
   2292                 R.layout.data_usage_app_title, root, false);
   2293         view.setText(label);
   2294         return view;
   2295     }
   2296 
   2297     /**
   2298      * Test if any networks are currently limited.
   2299      */
   2300     private boolean hasLimitedNetworks() {
   2301         return !buildLimitedNetworksList().isEmpty();
   2302     }
   2303 
   2304     /**
   2305      * Build string describing currently limited networks, which defines when
   2306      * background data is restricted.
   2307      */
   2308     @Deprecated
   2309     private CharSequence buildLimitedNetworksString() {
   2310         final List<CharSequence> limited = buildLimitedNetworksList();
   2311 
   2312         // handle case where no networks limited
   2313         if (limited.isEmpty()) {
   2314             limited.add(getText(R.string.data_usage_list_none));
   2315         }
   2316 
   2317         return TextUtils.join(limited);
   2318     }
   2319 
   2320     /**
   2321      * Build list of currently limited networks, which defines when background
   2322      * data is restricted.
   2323      */
   2324     @Deprecated
   2325     private List<CharSequence> buildLimitedNetworksList() {
   2326         final Context context = getActivity();
   2327 
   2328         // build combined list of all limited networks
   2329         final ArrayList<CharSequence> limited = Lists.newArrayList();
   2330 
   2331         final TelephonyManager tele = TelephonyManager.from(context);
   2332         if (tele.getSimState() == SIM_STATE_READY) {
   2333             final String subscriberId = getActiveSubscriberId(context);
   2334             if (mPolicyEditor.hasLimitedPolicy(buildTemplateMobileAll(subscriberId))) {
   2335                 limited.add(getText(R.string.data_usage_list_mobile));
   2336             }
   2337             if (mPolicyEditor.hasLimitedPolicy(buildTemplateMobile3gLower(subscriberId))) {
   2338                 limited.add(getText(R.string.data_usage_tab_3g));
   2339             }
   2340             if (mPolicyEditor.hasLimitedPolicy(buildTemplateMobile4g(subscriberId))) {
   2341                 limited.add(getText(R.string.data_usage_tab_4g));
   2342             }
   2343         }
   2344 
   2345         if (mPolicyEditor.hasLimitedPolicy(buildTemplateWifiWildcard())) {
   2346             limited.add(getText(R.string.data_usage_tab_wifi));
   2347         }
   2348         if (mPolicyEditor.hasLimitedPolicy(buildTemplateEthernet())) {
   2349             limited.add(getText(R.string.data_usage_tab_ethernet));
   2350         }
   2351 
   2352         return limited;
   2353     }
   2354 
   2355     /**
   2356      * Inset both selector and divider {@link Drawable} on the given
   2357      * {@link ListView} by the requested dimensions.
   2358      */
   2359     private static void insetListViewDrawables(ListView view, int insetSide) {
   2360         final Drawable selector = view.getSelector();
   2361         final Drawable divider = view.getDivider();
   2362 
   2363         // fully unregister these drawables so callbacks can be maintained after
   2364         // wrapping below.
   2365         final Drawable stub = new ColorDrawable(Color.TRANSPARENT);
   2366         view.setSelector(stub);
   2367         view.setDivider(stub);
   2368 
   2369         view.setSelector(new InsetBoundsDrawable(selector, insetSide));
   2370         view.setDivider(new InsetBoundsDrawable(divider, insetSide));
   2371     }
   2372 
   2373     /**
   2374      * Set {@link android.R.id#title} for a preference view inflated with
   2375      * {@link #inflatePreference(LayoutInflater, ViewGroup, View)}.
   2376      */
   2377     private static void setPreferenceTitle(View parent, int resId) {
   2378         final TextView title = (TextView) parent.findViewById(android.R.id.title);
   2379         title.setText(resId);
   2380     }
   2381 
   2382     /**
   2383      * Set {@link android.R.id#summary} for a preference view inflated with
   2384      * {@link #inflatePreference(LayoutInflater, ViewGroup, View)}.
   2385      */
   2386     private static void setPreferenceSummary(View parent, CharSequence string) {
   2387         final TextView summary = (TextView) parent.findViewById(android.R.id.summary);
   2388         summary.setVisibility(View.VISIBLE);
   2389         summary.setText(string);
   2390     }
   2391 }
   2392