Update data usage UX

Update the UX and dig the data usage screen out of a huge whole of
technical debt.  Switch every to use Preferences rather than standard
layouts and ListViews.

Split data usage into several fragments, all separated.

DataUsageSummary:
 - Shows a summary of the 'default' usage at the top, this will be
   the default sim on phones, or wifi if it has it, or ethernet
   as last attempt to show something.
 - Also has individual categories for each network type that has
   data, cell, wifi, and ethernet. Maybe should look into bt though?

DataUsageList:
- Takes a NetworkTemplate as an input, and can only be reached from
  the network specific categories in DataUsageSummary
- Shows a graph of current usage for that network and links to
  app detail page for any app.
- Has gear link to quick get to billing cycle screen if available

BillingCycleSettings:
 - Just a screen with the cycle day and warning/limits separated
   out from the data usage.

AppDataUsage:
 - App specific data usage details
 - May need some UX iteration given lack of clarity in the spec

Bug: 22459566
Change-Id: I0222d8d7ea7b75a9775207a6026ebbdcce8f5e46
diff --git a/src/com/android/settings/AppHeader.java b/src/com/android/settings/AppHeader.java
index 257b2f0..36026bb 100644
--- a/src/com/android/settings/AppHeader.java
+++ b/src/com/android/settings/AppHeader.java
@@ -57,7 +57,7 @@
                 tintColorRes, bar);
     }
 
-    private static View setupHeaderView(final Activity activity, Drawable icon, CharSequence label,
+    public static View setupHeaderView(final Activity activity, Drawable icon, CharSequence label,
             final String pkgName, final int uid, boolean includeAppInfo, int tintColorRes,
             View bar) {
         final ImageView appIcon = (ImageView) bar.findViewById(R.id.app_icon);
@@ -86,7 +86,7 @@
         return bar;
     }
 
-    private static boolean includeAppInfo(final Fragment fragment) {
+    public static boolean includeAppInfo(final Fragment fragment) {
         Bundle args = fragment.getArguments();
         boolean showInfo = true;
         if (args != null && args.getBoolean(EXTRA_HIDE_INFO_BUTTON, false)) {
diff --git a/src/com/android/settings/DataUsageSummary.java b/src/com/android/settings/DataUsageSummary.java
deleted file mode 100644
index c9a2999..0000000
--- a/src/com/android/settings/DataUsageSummary.java
+++ /dev/null
@@ -1,2848 +0,0 @@
-/*
- * Copyright (C) 2011 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.settings;
-
-import android.animation.LayoutTransition;
-import android.app.Activity;
-import android.app.ActivityManager;
-import android.app.AlertDialog;
-import android.app.Dialog;
-import android.app.DialogFragment;
-import android.app.Fragment;
-import android.app.FragmentTransaction;
-import android.app.LoaderManager.LoaderCallbacks;
-import android.content.ComponentName;
-import android.content.Context;
-import android.content.DialogInterface;
-import android.content.Intent;
-import android.content.Loader;
-import android.content.SharedPreferences;
-import android.content.pm.PackageManager;
-import android.content.pm.PackageManager.NameNotFoundException;
-import android.content.pm.UserInfo;
-import android.content.res.Resources;
-import android.graphics.Color;
-import android.graphics.drawable.ColorDrawable;
-import android.graphics.drawable.Drawable;
-import android.icu.impl.ICUResourceBundle;
-import android.icu.util.UResourceBundle;
-import android.net.ConnectivityManager;
-import android.net.INetworkPolicyManager;
-import android.net.INetworkStatsService;
-import android.net.INetworkStatsSession;
-import android.net.NetworkPolicy;
-import android.net.NetworkPolicyManager;
-import android.net.NetworkStats;
-import android.net.NetworkStatsHistory;
-import android.net.NetworkTemplate;
-import android.net.TrafficStats;
-import android.os.AsyncTask;
-import android.os.Bundle;
-import android.os.INetworkManagementService;
-import android.os.RemoteException;
-import android.os.ServiceManager;
-import android.os.SystemProperties;
-import android.os.UserHandle;
-import android.os.UserManager;
-import android.support.v7.preference.Preference;
-import android.telephony.SubscriptionInfo;
-import android.telephony.SubscriptionManager;
-import android.telephony.TelephonyManager;
-import android.text.TextUtils;
-import android.text.format.DateUtils;
-import android.text.format.Formatter;
-import android.text.format.Time;
-import android.util.Log;
-import android.util.SparseArray;
-import android.view.LayoutInflater;
-import android.view.Menu;
-import android.view.MenuInflater;
-import android.view.MenuItem;
-import android.view.View;
-import android.view.View.OnClickListener;
-import android.view.ViewGroup;
-import android.widget.AdapterView;
-import android.widget.AdapterView.OnItemClickListener;
-import android.widget.AdapterView.OnItemSelectedListener;
-import android.widget.ArrayAdapter;
-import android.widget.BaseAdapter;
-import android.widget.Button;
-import android.widget.FrameLayout;
-import android.widget.ImageView;
-import android.widget.LinearLayout;
-import android.widget.ListView;
-import android.widget.NumberPicker;
-import android.widget.ProgressBar;
-import android.widget.Spinner;
-import android.widget.Switch;
-import android.widget.TabHost;
-import android.widget.TabHost.OnTabChangeListener;
-import android.widget.TabHost.TabContentFactory;
-import android.widget.TabHost.TabSpec;
-import android.widget.TabWidget;
-import android.widget.TextView;
-import android.widget.Toast;
-import com.android.internal.logging.MetricsLogger;
-import com.android.internal.telephony.PhoneConstants;
-import com.android.settings.dashboard.SummaryLoader;
-import com.android.settings.dashboard.conditional.BackgroundDataCondition;
-import com.android.settings.dashboard.conditional.ConditionManager;
-import com.android.settings.drawable.InsetBoundsDrawable;
-import com.android.settings.net.DataUsageMeteredSettings;
-import com.android.settings.search.BaseSearchIndexProvider;
-import com.android.settings.search.Indexable;
-import com.android.settings.search.SearchIndexableRaw;
-import com.android.settings.widget.ChartDataUsageView;
-import com.android.settings.widget.ChartDataUsageView.DataUsageChartListener;
-import com.android.settings.widget.ChartNetworkSeriesView;
-import com.android.settingslib.AppItem;
-import com.android.settingslib.NetworkPolicyEditor;
-import com.android.settingslib.net.ChartData;
-import com.android.settingslib.net.ChartDataLoader;
-import com.android.settingslib.net.MobileDataController;
-import com.android.settingslib.net.SummaryForAllUidLoader;
-import com.android.settingslib.net.UidDetail;
-import com.android.settingslib.net.UidDetailProvider;
-import com.google.android.collect.Lists;
-import libcore.util.Objects;
-
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Locale;
-import java.util.Map;
-import java.util.Set;
-
-import static android.net.ConnectivityManager.TYPE_ETHERNET;
-import static android.net.ConnectivityManager.TYPE_MOBILE;
-import static android.net.ConnectivityManager.TYPE_WIFI;
-import static android.net.ConnectivityManager.TYPE_WIMAX;
-import static android.net.NetworkPolicy.LIMIT_DISABLED;
-import static android.net.NetworkPolicy.WARNING_DISABLED;
-import static android.net.NetworkPolicyManager.EXTRA_NETWORK_TEMPLATE;
-import static android.net.NetworkPolicyManager.POLICY_NONE;
-import static android.net.NetworkPolicyManager.POLICY_REJECT_METERED_BACKGROUND;
-import static android.net.NetworkPolicyManager.computeLastCycleBoundary;
-import static android.net.NetworkPolicyManager.computeNextCycleBoundary;
-import static android.net.NetworkTemplate.MATCH_MOBILE_3G_LOWER;
-import static android.net.NetworkTemplate.MATCH_MOBILE_4G;
-import static android.net.NetworkTemplate.MATCH_MOBILE_ALL;
-import static android.net.NetworkTemplate.MATCH_WIFI;
-import static android.net.NetworkTemplate.buildTemplateEthernet;
-import static android.net.NetworkTemplate.buildTemplateMobile3gLower;
-import static android.net.NetworkTemplate.buildTemplateMobile4g;
-import static android.net.NetworkTemplate.buildTemplateMobileAll;
-import static android.net.NetworkTemplate.buildTemplateWifiWildcard;
-import static android.net.TrafficStats.GB_IN_BYTES;
-import static android.net.TrafficStats.MB_IN_BYTES;
-import static android.net.TrafficStats.UID_REMOVED;
-import static android.net.TrafficStats.UID_TETHERING;
-import static android.telephony.TelephonyManager.SIM_STATE_READY;
-import static android.text.format.DateUtils.FORMAT_ABBREV_MONTH;
-import static android.text.format.DateUtils.FORMAT_SHOW_DATE;
-import static android.view.ViewGroup.LayoutParams.WRAP_CONTENT;
-import static com.android.internal.util.Preconditions.checkNotNull;
-import static com.android.settings.Utils.prepareCustomPreferencesList;
-
-/**
- * Panel showing data usage history across various networks, including options
- * to inspect based on usage cycle and control through {@link NetworkPolicy}.
- */
-public class DataUsageSummary extends HighlightingFragment implements Indexable {
-    private static final String TAG = "DataUsage";
-    private static final boolean LOGD = false;
-
-    // TODO: remove this testing code
-    private static final boolean TEST_ANIM = false;
-    private static final boolean TEST_RADIOS = false;
-
-    private static final String TEST_RADIOS_PROP = "test.radios";
-    private static final String TEST_SUBSCRIBER_PROP = "test.subscriberid";
-
-    private static final String TAB_3G = "3g";
-    private static final String TAB_4G = "4g";
-    private static final String TAB_MOBILE = "mobile";
-    private static final String TAB_WIFI = "wifi";
-    private static final String TAB_ETHERNET = "ethernet";
-
-    private static final String TAG_CONFIRM_DATA_DISABLE = "confirmDataDisable";
-    private static final String TAG_CONFIRM_LIMIT = "confirmLimit";
-    private static final String TAG_CYCLE_EDITOR = "cycleEditor";
-    private static final String TAG_WARNING_EDITOR = "warningEditor";
-    private static final String TAG_LIMIT_EDITOR = "limitEditor";
-    private static final String TAG_CONFIRM_RESTRICT = "confirmRestrict";
-    private static final String TAG_DENIED_RESTRICT = "deniedRestrict";
-    private static final String TAG_CONFIRM_APP_RESTRICT = "confirmAppRestrict";
-    private static final String TAG_APP_DETAILS = "appDetails";
-
-    private static final String DATA_USAGE_ENABLE_MOBILE_KEY = "data_usage_enable_mobile";
-    private static final String DATA_USAGE_DISABLE_MOBILE_LIMIT_KEY =
-            "data_usage_disable_mobile_limit";
-    private static final String DATA_USAGE_CYCLE_KEY = "data_usage_cycle";
-
-    public static final String EXTRA_SHOW_APP_IMMEDIATE_PKG = "showAppImmediatePkg";
-
-    private static final int LOADER_CHART_DATA = 2;
-    private static final int LOADER_SUMMARY = 3;
-
-    private INetworkManagementService mNetworkService;
-    private INetworkStatsService mStatsService;
-    private NetworkPolicyManager mPolicyManager;
-    private TelephonyManager mTelephonyManager;
-    private SubscriptionManager mSubscriptionManager;
-    private UserManager mUserManager;
-
-    private INetworkStatsSession mStatsSession;
-
-    private static final String PREF_FILE = "data_usage";
-    private static final String PREF_SHOW_WIFI = "show_wifi";
-    private static final String PREF_SHOW_ETHERNET = "show_ethernet";
-
-    private SharedPreferences mPrefs;
-
-    private TabHost mTabHost;
-    private ViewGroup mTabsContainer;
-    private TabWidget mTabWidget;
-    private ListView mListView;
-    private ChartNetworkSeriesView mSeries;
-    private ChartNetworkSeriesView mDetailedSeries;
-    private DataUsageAdapter mAdapter;
-
-    /** Distance to inset content from sides, when needed. */
-    private int mInsetSide = 0;
-
-    private ViewGroup mHeader;
-
-    private ViewGroup mNetworkSwitchesContainer;
-    private LinearLayout mNetworkSwitches;
-    private boolean mDataEnabledSupported;
-    private Switch mDataEnabled;
-    private View mDataEnabledView;
-    private boolean mDisableAtLimitSupported;
-    private Switch mDisableAtLimit;
-    private View mDisableAtLimitView;
-
-    private View mCycleView;
-    private Spinner mCycleSpinner;
-    private CycleAdapter mCycleAdapter;
-    private TextView mCycleSummary;
-
-    private ChartDataUsageView mChart;
-    private View mDisclaimer;
-    private TextView mEmpty;
-    private View mStupidPadding;
-
-    private View mAppDetail;
-    private ImageView mAppIcon;
-    private ViewGroup mAppTitles;
-    private TextView mAppTotal;
-    private TextView mAppForeground;
-    private TextView mAppBackground;
-    private Button mAppSettings;
-
-    private LinearLayout mAppSwitches;
-    private Switch mAppRestrict;
-    private View mAppRestrictView;
-
-    private boolean mShowWifi = false;
-    private boolean mShowEthernet = false;
-
-    private NetworkTemplate mTemplate;
-    private ChartData mChartData;
-
-    private AppItem mCurrentApp = null;
-
-    private Intent mAppSettingsIntent;
-
-    private NetworkPolicyEditor mPolicyEditor;
-
-    private String mCurrentTab = null;
-    private String mIntentTab = null;
-
-    private MenuItem mMenuRestrictBackground;
-    private MenuItem mMenuShowWifi;
-    private MenuItem mMenuShowEthernet;
-    private MenuItem mMenuSimCards;
-    private MenuItem mMenuCellularNetworks;
-
-    private List<SubscriptionInfo> mSubInfoList;
-    private Map<Integer,String> mMobileTagMap;
-
-    /** Flag used to ignore listeners during binding. */
-    private boolean mBinding;
-
-    private UidDetailProvider mUidDetailProvider;
-
-    // Indicates request to show app immediately rather than list.
-    private String mShowAppImmediatePkg;
-
-    /**
-     * Local cache of data enabled for subId, used to work around delays.
-     */
-    private final Map<String, Boolean> mMobileDataEnabled = new HashMap<String, Boolean>();
-
-    @Override
-    protected int getMetricsCategory() {
-        return MetricsLogger.DATA_USAGE_SUMMARY;
-    }
-
-    @Override
-    public void onCreate(Bundle savedInstanceState) {
-        super.onCreate(savedInstanceState);
-        final Context context = getActivity();
-
-        mNetworkService = INetworkManagementService.Stub.asInterface(
-                ServiceManager.getService(Context.NETWORKMANAGEMENT_SERVICE));
-        mStatsService = INetworkStatsService.Stub.asInterface(
-                ServiceManager.getService(Context.NETWORK_STATS_SERVICE));
-        mPolicyManager = NetworkPolicyManager.from(context);
-        mTelephonyManager = TelephonyManager.from(context);
-        mSubscriptionManager = SubscriptionManager.from(context);
-        mUserManager = UserManager.get(context);
-
-        mPrefs = getActivity().getSharedPreferences(PREF_FILE, Context.MODE_PRIVATE);
-
-        mPolicyEditor = new NetworkPolicyEditor(mPolicyManager);
-        mPolicyEditor.read();
-
-        mSubInfoList = mSubscriptionManager.getActiveSubscriptionInfoList();
-        mMobileTagMap = initMobileTabTag(mSubInfoList);
-
-        try {
-            if (!mNetworkService.isBandwidthControlEnabled()) {
-                Log.w(TAG, "No bandwidth control; leaving");
-                getActivity().finish();
-            }
-        } catch (RemoteException e) {
-            Log.w(TAG, "No bandwidth control; leaving");
-            getActivity().finish();
-        }
-
-        try {
-            mStatsSession = mStatsService.openSession();
-        } catch (RemoteException e) {
-            throw new RuntimeException(e);
-        }
-
-        mShowWifi = mPrefs.getBoolean(PREF_SHOW_WIFI, false);
-        mShowEthernet = mPrefs.getBoolean(PREF_SHOW_ETHERNET, false);
-
-        // override preferences when no mobile radio
-        if (!hasReadyMobileRadio(context)) {
-            mShowWifi = true;
-            mShowEthernet = true;
-        }
-
-        mUidDetailProvider = new UidDetailProvider(context);
-
-        Bundle arguments = getArguments();
-        if (arguments != null) {
-            mShowAppImmediatePkg = arguments.getString(EXTRA_SHOW_APP_IMMEDIATE_PKG);
-        }
-
-        setHasOptionsMenu(true);
-    }
-
-    @Override
-    public View onCreateView(LayoutInflater inflater, ViewGroup container,
-            Bundle savedInstanceState) {
-
-        final Context context = inflater.getContext();
-        final View view = inflater.inflate(R.layout.data_usage_summary, container, false);
-
-
-        mTabHost = (TabHost) view.findViewById(android.R.id.tabhost);
-        mTabsContainer = (ViewGroup) view.findViewById(R.id.tabs_container);
-        mTabWidget = (TabWidget) view.findViewById(android.R.id.tabs);
-        mListView = (ListView) view.findViewById(android.R.id.list);
-
-        // decide if we need to manually inset our content, or if we should rely
-        // on parent container for inset.
-        final boolean shouldInset = mListView.getScrollBarStyle()
-                == View.SCROLLBARS_OUTSIDE_OVERLAY;
-        mInsetSide = 0;
-
-        // adjust padding around tabwidget as needed
-        prepareCustomPreferencesList(container, view, mListView, false);
-
-        mTabHost.setup();
-        mTabHost.setOnTabChangedListener(mTabListener);
-
-        mHeader = (ViewGroup) inflater.inflate(R.layout.data_usage_header, mListView, false);
-        mHeader.setClickable(true);
-
-        mListView.addHeaderView(new View(context), null, true);
-        mListView.addHeaderView(mHeader, null, true);
-        mListView.setItemsCanFocus(true);
-
-        if (mInsetSide > 0) {
-            // inset selector and divider drawables
-            insetListViewDrawables(mListView, mInsetSide);
-            mHeader.setPaddingRelative(mInsetSide, 0, mInsetSide, 0);
-        }
-
-        {
-            // bind network switches
-            mNetworkSwitchesContainer = (ViewGroup) mHeader.findViewById(
-                    R.id.network_switches_container);
-            mNetworkSwitches = (LinearLayout) mHeader.findViewById(R.id.network_switches);
-
-            mDataEnabled = new Switch(inflater.getContext());
-            mDataEnabled.setClickable(false);
-            mDataEnabled.setFocusable(false);
-            mDataEnabledView = inflatePreference(inflater, mNetworkSwitches, mDataEnabled);
-            mDataEnabledView.setTag(R.id.preference_highlight_key,
-                    DATA_USAGE_ENABLE_MOBILE_KEY);
-            mDataEnabledView.setClickable(true);
-            mDataEnabledView.setFocusable(true);
-            mDataEnabledView.setOnClickListener(mDataEnabledListener);
-            mNetworkSwitches.addView(mDataEnabledView);
-
-            mDisableAtLimit = new Switch(inflater.getContext());
-            mDisableAtLimit.setClickable(false);
-            mDisableAtLimit.setFocusable(false);
-            mDisableAtLimitView = inflatePreference(inflater, mNetworkSwitches, mDisableAtLimit);
-            mDisableAtLimitView.setTag(R.id.preference_highlight_key,
-                    DATA_USAGE_DISABLE_MOBILE_LIMIT_KEY);
-            mDisableAtLimitView.setClickable(true);
-            mDisableAtLimitView.setFocusable(true);
-            mDisableAtLimitView.setOnClickListener(mDisableAtLimitListener);
-            mNetworkSwitches.addView(mDisableAtLimitView);
-
-            mCycleView = inflater.inflate(R.layout.data_usage_cycles, mNetworkSwitches, false);
-            mCycleView.setTag(R.id.preference_highlight_key, DATA_USAGE_CYCLE_KEY);
-            mCycleSpinner = (Spinner) mCycleView.findViewById(R.id.cycles_spinner);
-            mCycleAdapter = new CycleAdapter(context);
-            mCycleSpinner.setAdapter(mCycleAdapter);
-            mCycleSpinner.setOnItemSelectedListener(mCycleListener);
-            mCycleSummary = (TextView) mCycleView.findViewById(R.id.cycle_summary);
-            mNetworkSwitches.addView(mCycleView);
-            mSeries = (ChartNetworkSeriesView)view.findViewById(R.id.series);
-            mDetailedSeries = (ChartNetworkSeriesView)view.findViewById(R.id.detail_series);
-        }
-
-        mChart = (ChartDataUsageView) mHeader.findViewById(R.id.chart);
-        mChart.setListener(mChartListener);
-        mChart.bindNetworkPolicy(null);
-
-        {
-            // bind app detail controls
-            mAppDetail = mHeader.findViewById(R.id.app_detail);
-            mAppIcon = (ImageView) mAppDetail.findViewById(R.id.app_icon);
-            mAppTitles = (ViewGroup) mAppDetail.findViewById(R.id.app_titles);
-            mAppForeground = (TextView) mAppDetail.findViewById(R.id.app_foreground);
-            mAppBackground = (TextView) mAppDetail.findViewById(R.id.app_background);
-            mAppSwitches = (LinearLayout) mAppDetail.findViewById(R.id.app_switches);
-
-            mAppSettings = (Button) mAppDetail.findViewById(R.id.app_settings);
-
-            mAppRestrict = new Switch(inflater.getContext());
-            mAppRestrict.setClickable(false);
-            mAppRestrict.setFocusable(false);
-            mAppRestrictView = inflatePreference(inflater, mAppSwitches, mAppRestrict);
-            mAppRestrictView.setClickable(true);
-            mAppRestrictView.setFocusable(true);
-            mAppRestrictView.setOnClickListener(mAppRestrictListener);
-            mAppSwitches.addView(mAppRestrictView);
-        }
-
-        mDisclaimer = mHeader.findViewById(R.id.disclaimer);
-        mEmpty = (TextView) mHeader.findViewById(android.R.id.empty);
-        mStupidPadding = mHeader.findViewById(R.id.stupid_padding);
-
-        final UserManager um = (UserManager) context.getSystemService(Context.USER_SERVICE);
-        mAdapter = new DataUsageAdapter(um, mUidDetailProvider, mInsetSide);
-        mListView.setOnItemClickListener(mListListener);
-        mListView.setAdapter(mAdapter);
-
-        showRequestedAppIfNeeded(view);
-
-        return view;
-    }
-
-    private void showRequestedAppIfNeeded(View rootView) {
-        if (mShowAppImmediatePkg == null) {
-            return;
-        }
-        try {
-            int uid = getActivity().getPackageManager().getPackageUidAsUser(mShowAppImmediatePkg,
-                UserHandle.myUserId());
-            AppItem app = new AppItem(uid);
-            app.addUid(uid);
-
-            final UidDetail detail = mUidDetailProvider.getUidDetail(app.key, true);
-            // When we are going straight to an app then we are coming from App Info and want
-            // a header at the top.
-            FrameLayout pinnedHeader = (FrameLayout) rootView.findViewById(R.id.pinned_header);
-            AppHeader.createAppHeader(getActivity(), detail.icon, detail.label,
-                    mShowAppImmediatePkg, uid, pinnedHeader);
-            AppDetailsFragment.show(DataUsageSummary.this, app, detail.label, false);
-        } catch (NameNotFoundException e) {
-            Log.w(TAG, "Could not find " + mShowAppImmediatePkg, e);
-            Toast.makeText(getActivity(), getString(R.string.unknown_app), Toast.LENGTH_LONG)
-                    .show();
-            getActivity().finish();
-        }
-    }
-
-    @Override
-    public void onViewStateRestored(Bundle savedInstanceState) {
-        super.onViewStateRestored(savedInstanceState);
-
-        // pick default tab based on incoming intent
-        final Intent intent = getActivity().getIntent();
-        mIntentTab = computeTabFromIntent(intent);
-
-        // this kicks off chain reaction which creates tabs, binds the body to
-        // selected network, and binds chart, cycles and detail list.
-        updateTabs();
-    }
-
-    @Override
-    public void onResume() {
-        super.onResume();
-
-        getView().post(new Runnable() {
-            @Override
-            public void run() {
-                highlightViewIfNeeded();
-            }
-        });
-
-        // kick off background task to update stats
-        new AsyncTask<Void, Void, Void>() {
-            @Override
-            protected Void doInBackground(Void... params) {
-                try {
-                    // wait a few seconds before kicking off
-                    Thread.sleep(2 * DateUtils.SECOND_IN_MILLIS);
-                    mStatsService.forceUpdate();
-                } catch (InterruptedException e) {
-                } catch (RemoteException e) {
-                }
-                return null;
-            }
-
-            @Override
-            protected void onPostExecute(Void result) {
-                if (isAdded()) {
-                    updateBody();
-                }
-            }
-        }.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
-    }
-
-    @Override
-    public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
-        inflater.inflate(R.menu.data_usage, menu);
-    }
-
-    @Override
-    public void onPrepareOptionsMenu(Menu menu) {
-        final Context context = getContext();
-        if (context == null) return;
-        final boolean appDetailMode = isAppDetailMode();
-        final boolean isAdmin = mUserManager.isAdminUser();
-
-        mMenuShowWifi = menu.findItem(R.id.data_usage_menu_show_wifi);
-        if (hasWifiRadio(context) && hasReadyMobileRadio(context)) {
-            mMenuShowWifi.setVisible(!appDetailMode);
-        } else {
-            mMenuShowWifi.setVisible(false);
-        }
-
-        mMenuShowEthernet = menu.findItem(R.id.data_usage_menu_show_ethernet);
-        if (hasEthernet(context) && hasReadyMobileRadio(context)) {
-            mMenuShowEthernet.setVisible(!appDetailMode);
-        } else {
-            mMenuShowEthernet.setVisible(false);
-        }
-
-        mMenuRestrictBackground = menu.findItem(R.id.data_usage_menu_restrict_background);
-        mMenuRestrictBackground.setVisible(
-                hasReadyMobileRadio(context) && isAdmin && !appDetailMode);
-
-        final MenuItem metered = menu.findItem(R.id.data_usage_menu_metered);
-        if (hasReadyMobileRadio(context) || hasWifiRadio(context)) {
-            metered.setVisible(!appDetailMode);
-        } else {
-            metered.setVisible(false);
-        }
-
-        // TODO: show when multiple sims available
-        mMenuSimCards = menu.findItem(R.id.data_usage_menu_sim_cards);
-        mMenuSimCards.setVisible(false);
-
-        mMenuCellularNetworks = menu.findItem(R.id.data_usage_menu_cellular_networks);
-        mMenuCellularNetworks.setVisible(hasReadyMobileRadio(context)
-                && !appDetailMode && isAdmin);
-
-        final MenuItem help = menu.findItem(R.id.data_usage_menu_help);
-        String helpUrl;
-        if (!TextUtils.isEmpty(helpUrl = getResources().getString(R.string.help_url_data_usage))) {
-            HelpUtils.prepareHelpMenuItem(getActivity(), help, helpUrl, getClass().getName());
-        } else {
-            help.setVisible(false);
-        }
-
-        updateMenuTitles();
-    }
-
-    private void updateMenuTitles() {
-        if (mPolicyManager.getRestrictBackground()) {
-            mMenuRestrictBackground.setTitle(R.string.data_usage_menu_allow_background);
-        } else {
-            mMenuRestrictBackground.setTitle(R.string.data_usage_menu_restrict_background);
-        }
-
-        if (mShowWifi) {
-            mMenuShowWifi.setTitle(R.string.data_usage_menu_hide_wifi);
-        } else {
-            mMenuShowWifi.setTitle(R.string.data_usage_menu_show_wifi);
-        }
-
-        if (mShowEthernet) {
-            mMenuShowEthernet.setTitle(R.string.data_usage_menu_hide_ethernet);
-        } else {
-            mMenuShowEthernet.setTitle(R.string.data_usage_menu_show_ethernet);
-        }
-    }
-
-    @Override
-    public boolean onOptionsItemSelected(MenuItem item) {
-        switch (item.getItemId()) {
-            case R.id.data_usage_menu_restrict_background: {
-                final boolean restrictBackground = !mPolicyManager.getRestrictBackground();
-                if (restrictBackground) {
-                    ConfirmRestrictFragment.show(this);
-                } else {
-                    // no confirmation to drop restriction
-                    setRestrictBackground(false);
-                }
-                return true;
-            }
-            case R.id.data_usage_menu_show_wifi: {
-                mShowWifi = !mShowWifi;
-                mPrefs.edit().putBoolean(PREF_SHOW_WIFI, mShowWifi).apply();
-                updateMenuTitles();
-                updateTabs();
-                return true;
-            }
-            case R.id.data_usage_menu_show_ethernet: {
-                mShowEthernet = !mShowEthernet;
-                mPrefs.edit().putBoolean(PREF_SHOW_ETHERNET, mShowEthernet).apply();
-                updateMenuTitles();
-                updateTabs();
-                return true;
-            }
-            case R.id.data_usage_menu_sim_cards: {
-                // TODO: hook up to sim cards
-                return true;
-            }
-            case R.id.data_usage_menu_cellular_networks: {
-                final Intent intent = new Intent(Intent.ACTION_MAIN);
-                intent.setComponent(new ComponentName("com.android.phone",
-                        "com.android.phone.MobileNetworkSettings"));
-                startActivity(intent);
-                return true;
-            }
-            case R.id.data_usage_menu_metered: {
-                final SettingsActivity sa = (SettingsActivity) getActivity();
-                sa.startPreferencePanel(DataUsageMeteredSettings.class.getCanonicalName(), null,
-                        R.string.data_usage_metered_title, null, this, 0);
-                return true;
-            }
-        }
-        return false;
-    }
-
-    @Override
-    public void onDestroy() {
-        mDataEnabledView = null;
-        mDisableAtLimitView = null;
-
-        mUidDetailProvider.clearCache();
-        mUidDetailProvider = null;
-
-        TrafficStats.closeQuietly(mStatsSession);
-
-        super.onDestroy();
-    }
-
-    /**
-     * Build and assign {@link LayoutTransition} to various containers. Should
-     * only be assigned after initial layout is complete.
-     */
-    private void ensureLayoutTransitions() {
-        if (mShowAppImmediatePkg != null) {
-            // If we are skipping right to showing an app, we don't care about transitions.
-            return;
-        }
-        // skip when already setup
-        if (mChart.getLayoutTransition() != null) return;
-
-        mTabsContainer.setLayoutTransition(buildLayoutTransition());
-        mHeader.setLayoutTransition(buildLayoutTransition());
-        mNetworkSwitchesContainer.setLayoutTransition(buildLayoutTransition());
-
-        final LayoutTransition chartTransition = buildLayoutTransition();
-        chartTransition.disableTransitionType(LayoutTransition.APPEARING);
-        chartTransition.disableTransitionType(LayoutTransition.DISAPPEARING);
-        mChart.setLayoutTransition(chartTransition);
-    }
-
-    private static LayoutTransition buildLayoutTransition() {
-        final LayoutTransition transition = new LayoutTransition();
-        if (TEST_ANIM) {
-            transition.setDuration(1500);
-        }
-        transition.setAnimateParentHierarchy(false);
-        return transition;
-    }
-
-    /**
-     * Rebuild all tabs based on {@link NetworkPolicyEditor} and
-     * {@link #mShowWifi}, hiding the tabs entirely when applicable. Selects
-     * first tab, and kicks off a full rebind of body contents.
-     */
-    private void updateTabs() {
-        final Context context = getActivity();
-        mTabHost.clearAllTabs();
-
-        int simCount = mTelephonyManager.getSimCount();
-
-        List<SubscriptionInfo> sirs = mSubscriptionManager.getActiveSubscriptionInfoList();
-        if (sirs != null) {
-            for (SubscriptionInfo sir : sirs) {
-                addMobileTab(context, sir, (simCount > 1));
-            }
-        }
-
-        if (mShowWifi && hasWifiRadio(context)) {
-            mTabHost.addTab(buildTabSpec(TAB_WIFI, R.string.data_usage_tab_wifi));
-        }
-
-        if (mShowEthernet && hasEthernet(context)) {
-            mTabHost.addTab(buildTabSpec(TAB_ETHERNET, R.string.data_usage_tab_ethernet));
-        }
-
-        final boolean noTabs = mTabWidget.getTabCount() == 0;
-        final boolean multipleTabs = mTabWidget.getTabCount() > 1;
-        mTabWidget.setVisibility(multipleTabs ? View.VISIBLE : View.GONE);
-        if (mIntentTab != null) {
-            if (Objects.equal(mIntentTab, mTabHost.getCurrentTabTag())) {
-                // already hit updateBody() when added; ignore
-                updateBody();
-            } else {
-                mTabHost.setCurrentTabByTag(mIntentTab);
-            }
-            mIntentTab = null;
-        } else if (noTabs) {
-            // no usable tabs, so hide body
-            updateBody();
-        } else {
-            // already hit updateBody() when added; ignore
-        }
-    }
-
-    /**
-     * Factory that provide empty {@link View} to make {@link TabHost} happy.
-     */
-    private TabContentFactory mEmptyTabContent = new TabContentFactory() {
-        @Override
-        public View createTabContent(String tag) {
-            return new View(mTabHost.getContext());
-        }
-    };
-
-    /**
-     * Build {@link TabSpec} with thin indicator, and empty content.
-     */
-    private TabSpec buildTabSpec(String tag, int titleRes) {
-        return mTabHost.newTabSpec(tag).setIndicator(getText(titleRes)).setContent(
-                mEmptyTabContent);
-    }
-
-    /**
-     * Build {@link TabSpec} with thin indicator, and empty content.
-     */
-    private TabSpec buildTabSpec(String tag, CharSequence title) {
-        return mTabHost.newTabSpec(tag).setIndicator(title).setContent(
-                mEmptyTabContent);
-    }
-
-
-    private OnTabChangeListener mTabListener = new OnTabChangeListener() {
-        @Override
-        public void onTabChanged(String tabId) {
-            // user changed tab; update body
-            updateBody();
-        }
-    };
-
-    /**
-     * Update body content based on current tab. Loads
-     * {@link NetworkStatsHistory} and {@link NetworkPolicy} from system, and
-     * binds them to visible controls.
-     */
-    private void updateBody() {
-        mBinding = true;
-        if (!isAdded()) return;
-
-        final Context context = getActivity();
-        final String currentTab = mTabHost.getCurrentTabTag();
-        final boolean isAdmin = mUserManager.isAdminUser();
-
-        if (currentTab == null) {
-            Log.w(TAG, "no tab selected; hiding body");
-            mListView.setVisibility(View.GONE);
-            return;
-        } else {
-            mListView.setVisibility(View.VISIBLE);
-        }
-
-        mCurrentTab = currentTab;
-
-        if (LOGD) Log.d(TAG, "updateBody() with currentTab=" + currentTab);
-
-        mDataEnabledSupported = isAdmin;
-        mDisableAtLimitSupported = true;
-
-        // TODO: remove mobile tabs when SIM isn't ready probably by
-        // TODO: using SubscriptionManager.getActiveSubscriptionInfoList.
-        if (LOGD) Log.d(TAG, "updateBody() isMobileTab=" + isMobileTab(currentTab));
-
-        if (isMobileTab(currentTab)) {
-            if (LOGD) Log.d(TAG, "updateBody() mobile tab");
-            setPreferenceTitle(mDataEnabledView, R.string.data_usage_enable_mobile);
-            setPreferenceTitle(mDisableAtLimitView, R.string.data_usage_disable_mobile_limit);
-            mDataEnabledSupported = isMobileDataAvailable(getSubId(currentTab));
-
-            // Match mobile traffic for this subscriber, but normalize it to
-            // catch any other merged subscribers.
-            mTemplate = buildTemplateMobileAll(
-                    getActiveSubscriberId(context, getSubId(currentTab)));
-            mTemplate = NetworkTemplate.normalize(mTemplate,
-                    mTelephonyManager.getMergedSubscriberIds());
-
-        } else if (TAB_3G.equals(currentTab)) {
-            if (LOGD) Log.d(TAG, "updateBody() 3g tab");
-            setPreferenceTitle(mDataEnabledView, R.string.data_usage_enable_3g);
-            setPreferenceTitle(mDisableAtLimitView, R.string.data_usage_disable_3g_limit);
-            // TODO: bind mDataEnabled to 3G radio state
-            mTemplate = buildTemplateMobile3gLower(getActiveSubscriberId(context));
-
-        } else if (TAB_4G.equals(currentTab)) {
-            if (LOGD) Log.d(TAG, "updateBody() 4g tab");
-            setPreferenceTitle(mDataEnabledView, R.string.data_usage_enable_4g);
-            setPreferenceTitle(mDisableAtLimitView, R.string.data_usage_disable_4g_limit);
-            // TODO: bind mDataEnabled to 4G radio state
-            mTemplate = buildTemplateMobile4g(getActiveSubscriberId(context));
-
-        } else if (TAB_WIFI.equals(currentTab)) {
-            // wifi doesn't have any controls
-            if (LOGD) Log.d(TAG, "updateBody() wifi tab");
-            mDataEnabledSupported = false;
-            mDisableAtLimitSupported = false;
-            mTemplate = buildTemplateWifiWildcard();
-
-        } else if (TAB_ETHERNET.equals(currentTab)) {
-            // ethernet doesn't have any controls
-            if (LOGD) Log.d(TAG, "updateBody() ethernet tab");
-            mDataEnabledSupported = false;
-            mDisableAtLimitSupported = false;
-            mTemplate = buildTemplateEthernet();
-
-        } else {
-            if (LOGD) Log.d(TAG, "updateBody() unknown tab");
-            throw new IllegalStateException("unknown tab: " + currentTab);
-        }
-
-        mPolicyEditor.read();
-        final NetworkPolicy policy = mPolicyEditor.getPolicy(mTemplate);
-        if (policy != null) {
-            final long currentTime = System.currentTimeMillis();
-            final long start = computeLastCycleBoundary(currentTime, policy);
-            final long end = currentTime;
-            long totalBytes = 0;
-
-            try {
-                totalBytes = mStatsService.getNetworkTotalBytes(policy.template, start, end);
-            } catch (RuntimeException e) {
-            } catch (RemoteException e) {
-            }
-
-            if (policy.isOverLimit(totalBytes) && policy.lastLimitSnooze < start) {
-                setPreferenceSummary(mDataEnabledView,
-                        getString(R.string.data_usage_cellular_data_summary));
-            } else {
-                final TextView summary = (TextView) mDataEnabledView
-                        .findViewById(android.R.id.summary);
-                summary.setVisibility(View.GONE);
-            }
-        }
-
-        // kick off loader for network history
-        // TODO: consider chaining two loaders together instead of reloading
-        // network history when showing app detail.
-        getLoaderManager().restartLoader(LOADER_CHART_DATA,
-                ChartDataLoader.buildArgs(mTemplate, mCurrentApp), mChartDataCallbacks);
-
-        // detail mode can change visible menus, invalidate
-        getActivity().invalidateOptionsMenu();
-
-        mBinding = false;
-
-        int seriesColor = context.getColor(R.color.sim_noitification);
-        if (mCurrentTab != null && mCurrentTab.length() > TAB_MOBILE.length() ){
-            final int slotId = Integer.parseInt(mCurrentTab.substring(TAB_MOBILE.length(),
-                    mCurrentTab.length()));
-            final SubscriptionInfo sir = mSubscriptionManager
-                    .getActiveSubscriptionInfoForSimSlotIndex(slotId);
-
-            if (sir != null) {
-                seriesColor = sir.getIconTint();
-            }
-        }
-
-        final int secondaryColor = Color.argb(127, Color.red(seriesColor), Color.green(seriesColor),
-                Color.blue(seriesColor));
-        mSeries.setChartColor(Color.BLACK, seriesColor, secondaryColor);
-        mDetailedSeries.setChartColor(Color.BLACK, seriesColor, secondaryColor);
-    }
-
-    private boolean isAppDetailMode() {
-        return mCurrentApp != null;
-    }
-
-    /**
-     * Update UID details panels to match {@link #mCurrentApp}, showing or
-     * hiding them depending on {@link #isAppDetailMode()}.
-     */
-    private void updateAppDetail() {
-        final Context context = getActivity();
-        final PackageManager pm = context.getPackageManager();
-        final LayoutInflater inflater = getActivity().getLayoutInflater();
-
-        if (isAppDetailMode()) {
-            mAppDetail.setVisibility(View.VISIBLE);
-            mCycleAdapter.setChangeVisible(false);
-            mChart.setVisibility(View.GONE);
-        } else {
-            mAppDetail.setVisibility(View.GONE);
-            mCycleAdapter.setChangeVisible(true);
-            mChart.setVisibility(View.VISIBLE);
-
-            // hide detail stats when not in detail mode
-            mChart.bindDetailNetworkStats(null);
-            return;
-        }
-
-        // remove warning/limit sweeps while in detail mode
-        mChart.bindNetworkPolicy(null);
-
-        // show icon and all labels appearing under this app
-        final int uid = mCurrentApp.key;
-        final UidDetail detail = mUidDetailProvider.getUidDetail(uid, true);
-        mAppIcon.setImageDrawable(detail.icon);
-
-        mAppTitles.removeAllViews();
-
-        View title = null;
-        if (detail.detailLabels != null) {
-            final int n = detail.detailLabels.length;
-            for (int i = 0; i < n; ++i) {
-                CharSequence label = detail.detailLabels[i];
-                CharSequence contentDescription = detail.detailContentDescriptions[i];
-                title = inflater.inflate(R.layout.data_usage_app_title, mAppTitles, false);
-                TextView appTitle = (TextView) title.findViewById(R.id.app_title);
-                appTitle.setText(label);
-                appTitle.setContentDescription(contentDescription);
-                mAppTitles.addView(title);
-            }
-        } else {
-            title = inflater.inflate(R.layout.data_usage_app_title, mAppTitles, false);
-            TextView appTitle = (TextView) title.findViewById(R.id.app_title);
-            appTitle.setText(detail.label);
-            appTitle.setContentDescription(detail.contentDescription);
-            mAppTitles.addView(title);
-        }
-
-        // Remember last slot for summary
-        if (title != null) {
-            mAppTotal = (TextView) title.findViewById(R.id.app_summary);
-        } else {
-            mAppTotal = null;
-        }
-
-        // enable settings button when package provides it
-        final String[] packageNames = pm.getPackagesForUid(uid);
-        if (packageNames != null && packageNames.length > 0) {
-            mAppSettingsIntent = new Intent(Intent.ACTION_MANAGE_NETWORK_USAGE);
-            mAppSettingsIntent.addCategory(Intent.CATEGORY_DEFAULT);
-
-            // Search for match across all packages
-            boolean matchFound = false;
-            for (String packageName : packageNames) {
-                mAppSettingsIntent.setPackage(packageName);
-                if (pm.resolveActivity(mAppSettingsIntent, 0) != null) {
-                    matchFound = true;
-                    break;
-                }
-            }
-
-            mAppSettings.setOnClickListener(new OnClickListener() {
-                @Override
-                public void onClick(View v) {
-                    if (!isAdded()) {
-                        return;
-                    }
-
-                    // TODO: target towards entire UID instead of just first package
-                    getActivity().startActivityAsUser(mAppSettingsIntent,
-                            new UserHandle(UserHandle.getUserId(uid)));
-                }
-            });
-            mAppSettings.setEnabled(matchFound);
-            mAppSettings.setVisibility(View.VISIBLE);
-
-        } else {
-            mAppSettingsIntent = null;
-            mAppSettings.setOnClickListener(null);
-            mAppSettings.setVisibility(View.GONE);
-        }
-
-        updateDetailData();
-
-        if (UserHandle.isApp(uid) && !mPolicyManager.getRestrictBackground()
-                && isBandwidthControlEnabled() && hasReadyMobileRadio(context)) {
-            setPreferenceTitle(mAppRestrictView, R.string.data_usage_app_restrict_background);
-            setPreferenceSummary(mAppRestrictView,
-                    getString(R.string.data_usage_app_restrict_background_summary));
-
-            mAppRestrictView.setVisibility(View.VISIBLE);
-            mAppRestrict.setChecked(getAppRestrictBackground());
-
-        } else {
-            mAppRestrictView.setVisibility(View.GONE);
-        }
-    }
-
-    private void setPolicyWarningBytes(long warningBytes) {
-        if (LOGD) Log.d(TAG, "setPolicyWarningBytes()");
-        mPolicyEditor.setPolicyWarningBytes(mTemplate, warningBytes);
-        updatePolicy(false);
-    }
-
-    private void setPolicyLimitBytes(long limitBytes) {
-        if (LOGD) Log.d(TAG, "setPolicyLimitBytes()");
-        mPolicyEditor.setPolicyLimitBytes(mTemplate, limitBytes);
-        updatePolicy(false);
-    }
-
-    private boolean isMobileDataEnabled(int subId) {
-        if (LOGD) Log.d(TAG, "isMobileDataEnabled:+ subId=" + subId);
-        boolean isEnable = false;
-        if (mMobileDataEnabled.get(String.valueOf(subId)) != null) {
-            //TODO: deprecate and remove this once enabled flag is on policy
-            //Multiple Subscriptions, the value need to be reseted
-            isEnable = mMobileDataEnabled.get(String.valueOf(subId)).booleanValue();
-            if (LOGD) {
-                Log.d(TAG, "isMobileDataEnabled: != null, subId=" + subId
-                        + " isEnable=" + isEnable);
-            }
-            mMobileDataEnabled.put(String.valueOf(subId), null);
-        } else {
-            // SUB SELECT
-            isEnable = mTelephonyManager.getDataEnabled(subId);
-            if (LOGD) {
-                Log.d(TAG, "isMobileDataEnabled: == null, subId=" + subId
-                        + " isEnable=" + isEnable);
-            }
-        }
-        return isEnable;
-    }
-
-    private void setMobileDataEnabled(int subId, boolean enabled) {
-        if (LOGD) Log.d(TAG, "setMobileDataEnabled()");
-        mTelephonyManager.setDataEnabled(subId, enabled);
-        mMobileDataEnabled.put(String.valueOf(subId), enabled);
-        updatePolicy(false);
-    }
-
-    private boolean isNetworkPolicyModifiable(NetworkPolicy policy) {
-        return policy != null && isBandwidthControlEnabled() && mDataEnabled.isChecked()
-                && mUserManager.isAdminUser();
-    }
-
-    private boolean isBandwidthControlEnabled() {
-        try {
-            return mNetworkService.isBandwidthControlEnabled();
-        } catch (RemoteException e) {
-            Log.w(TAG, "problem talking with INetworkManagementService: " + e);
-            return false;
-        }
-    }
-
-    public void setRestrictBackground(boolean restrictBackground) {
-        mPolicyManager.setRestrictBackground(restrictBackground);
-        updateMenuTitles();
-        ConditionManager.get(getContext()).getCondition(BackgroundDataCondition.class)
-                .refreshState();
-    }
-
-    private boolean getAppRestrictBackground() {
-        final int uid = mCurrentApp.key;
-        final int uidPolicy = mPolicyManager.getUidPolicy(uid);
-        return (uidPolicy & POLICY_REJECT_METERED_BACKGROUND) != 0;
-    }
-
-    private void setAppRestrictBackground(boolean restrictBackground) {
-        if (LOGD) Log.d(TAG, "setAppRestrictBackground()");
-        final int uid = mCurrentApp.key;
-        mPolicyManager.setUidPolicy(
-                uid, restrictBackground ? POLICY_REJECT_METERED_BACKGROUND : POLICY_NONE);
-        mAppRestrict.setChecked(restrictBackground);
-    }
-
-    /**
-     * Update chart sweeps and cycle list to reflect {@link NetworkPolicy} for
-     * current {@link #mTemplate}.
-     */
-    private void updatePolicy(boolean refreshCycle) {
-        boolean dataEnabledVisible = mDataEnabledSupported;
-        boolean disableAtLimitVisible = mDisableAtLimitSupported;
-
-        if (isAppDetailMode()) {
-            dataEnabledVisible = false;
-            disableAtLimitVisible = false;
-        }
-
-        // TODO: move enabled state directly into policy
-        if (isMobileTab(mCurrentTab)) {
-            mBinding = true;
-            mDataEnabled.setChecked(isMobileDataEnabled(getSubId(mCurrentTab)));
-            mBinding = false;
-        }
-
-        final NetworkPolicy policy = mPolicyEditor.getPolicy(mTemplate);
-        //SUB SELECT
-        if (isNetworkPolicyModifiable(policy) && isMobileDataAvailable(getSubId(mCurrentTab))) {
-            mDisableAtLimit.setChecked(policy != null && policy.limitBytes != LIMIT_DISABLED);
-            if (!isAppDetailMode()) {
-                mChart.bindNetworkPolicy(policy);
-            }
-
-        } else {
-            // controls are disabled; don't bind warning/limit sweeps
-            disableAtLimitVisible = false;
-            mChart.bindNetworkPolicy(null);
-        }
-
-        mDataEnabledView.setVisibility(dataEnabledVisible ? View.VISIBLE : View.GONE);
-        mDisableAtLimitView.setVisibility(disableAtLimitVisible ? View.VISIBLE : View.GONE);
-
-        if (refreshCycle) {
-            // generate cycle list based on policy and available history
-            updateCycleList(policy);
-        }
-    }
-
-    /**
-     * Rebuild {@link #mCycleAdapter} based on {@link NetworkPolicy#cycleDay}
-     * and available {@link NetworkStatsHistory} data. Always selects the newest
-     * item, updating the inspection range on {@link #mChart}.
-     */
-    private void updateCycleList(NetworkPolicy policy) {
-        // stash away currently selected cycle to try restoring below
-        final CycleItem previousItem = (CycleItem) mCycleSpinner.getSelectedItem();
-        mCycleAdapter.clear();
-
-        final Context context = mCycleSpinner.getContext();
-        NetworkStatsHistory.Entry entry = null;
-
-        long historyStart = Long.MAX_VALUE;
-        long historyEnd = Long.MIN_VALUE;
-        if (mChartData != null) {
-            historyStart = mChartData.network.getStart();
-            historyEnd = mChartData.network.getEnd();
-        }
-
-        final long now = System.currentTimeMillis();
-        if (historyStart == Long.MAX_VALUE) historyStart = now;
-        if (historyEnd == Long.MIN_VALUE) historyEnd = now + 1;
-
-        boolean hasCycles = false;
-        if (policy != null) {
-            // find the next cycle boundary
-            long cycleEnd = computeNextCycleBoundary(historyEnd, policy);
-
-            // walk backwards, generating all valid cycle ranges
-            while (cycleEnd > historyStart) {
-                final long cycleStart = computeLastCycleBoundary(cycleEnd, policy);
-                Log.d(TAG, "generating cs=" + cycleStart + " to ce=" + cycleEnd + " waiting for hs="
-                        + historyStart);
-
-                final boolean includeCycle;
-                if (mChartData != null) {
-                    entry = mChartData.network.getValues(cycleStart, cycleEnd, entry);
-                    includeCycle = (entry.rxBytes + entry.txBytes) > 0;
-                } else {
-                    includeCycle = true;
-                }
-
-                if (includeCycle) {
-                    mCycleAdapter.add(new CycleItem(context, cycleStart, cycleEnd));
-                    hasCycles = true;
-                }
-                cycleEnd = cycleStart;
-            }
-
-            // one last cycle entry to modify policy cycle day
-            mCycleAdapter.setChangePossible(isNetworkPolicyModifiable(policy));
-        }
-
-        if (!hasCycles) {
-            // no policy defined cycles; show entry for each four-week period
-            long cycleEnd = historyEnd;
-            while (cycleEnd > historyStart) {
-                final long cycleStart = cycleEnd - (DateUtils.WEEK_IN_MILLIS * 4);
-
-                final boolean includeCycle;
-                if (mChartData != null) {
-                    entry = mChartData.network.getValues(cycleStart, cycleEnd, entry);
-                    includeCycle = (entry.rxBytes + entry.txBytes) > 0;
-                } else {
-                    includeCycle = true;
-                }
-
-                if (includeCycle) {
-                    mCycleAdapter.add(new CycleItem(context, cycleStart, cycleEnd));
-                }
-                cycleEnd = cycleStart;
-            }
-
-            mCycleAdapter.setChangePossible(false);
-        }
-
-        // force pick the current cycle (first item)
-        if (mCycleAdapter.getCount() > 0) {
-            final int position = mCycleAdapter.findNearestPosition(previousItem);
-            mCycleSpinner.setSelection(position);
-
-            // only force-update cycle when changed; skipping preserves any
-            // user-defined inspection region.
-            final CycleItem selectedItem = mCycleAdapter.getItem(position);
-            if (!Objects.equal(selectedItem, previousItem)) {
-                mCycleListener.onItemSelected(mCycleSpinner, null, position, 0);
-            } else {
-                // but still kick off loader for detailed list
-                updateDetailData();
-            }
-        } else {
-            updateDetailData();
-        }
-    }
-
-    private void disableDataForOtherSubscriptions(SubscriptionInfo currentSir) {
-        if (mSubInfoList != null) {
-            for (SubscriptionInfo subInfo : mSubInfoList) {
-                if (subInfo.getSubscriptionId() != currentSir.getSubscriptionId()) {
-                    setMobileDataEnabled(subInfo.getSubscriptionId(), false);
-                }
-            }
-        }
-    }
-
-    private View.OnClickListener mDataEnabledListener = new View.OnClickListener() {
-        @Override
-        public void onClick(View v) {
-            if (mBinding) return;
-
-            final boolean enabled = !mDataEnabled.isChecked();
-            final String currentTab = mCurrentTab;
-            if (isMobileTab(currentTab)) {
-                MetricsLogger.action(getContext(), MetricsLogger.ACTION_CELL_DATA_TOGGLE, enabled);
-                if (enabled) {
-                    // If we are showing the Sim Card tile then we are a Multi-Sim device.
-                    if (Utils.showSimCardTile(getActivity())) {
-                        handleMultiSimDataDialog();
-                    } else {
-                        setMobileDataEnabled(getSubId(currentTab), true);
-                    }
-                } else {
-                    // disabling data; show confirmation dialog which eventually
-                    // calls setMobileDataEnabled() once user confirms.
-                    ConfirmDataDisableFragment.show(DataUsageSummary.this, getSubId(mCurrentTab));
-                }
-            }
-
-            updatePolicy(false);
-        }
-    };
-
-    private void handleMultiSimDataDialog() {
-        final Context context = getActivity();
-        final SubscriptionInfo currentSir = getCurrentTabSubInfo(context);
-
-        //If sim has not loaded after toggling data switch, return.
-        if (currentSir == null) {
-            return;
-        }
-
-        final SubscriptionInfo nextSir = mSubscriptionManager.getDefaultDataSubscriptionInfo();
-
-        // If the device is single SIM or is enabling data on the active data SIM then forgo
-        // the pop-up.
-        if (!Utils.showSimCardTile(context) ||
-                (nextSir != null && currentSir != null &&
-                currentSir.getSubscriptionId() == nextSir.getSubscriptionId())) {
-            setMobileDataEnabled(currentSir.getSubscriptionId(), true);
-            if (nextSir != null && currentSir != null &&
-                currentSir.getSubscriptionId() == nextSir.getSubscriptionId()) {
-                disableDataForOtherSubscriptions(currentSir);
-            }
-            updateBody();
-            return;
-        }
-
-        final String previousName = (nextSir == null)
-            ? context.getResources().getString(R.string.sim_selection_required_pref)
-            : nextSir.getDisplayName().toString();
-
-        AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
-
-        builder.setTitle(R.string.sim_change_data_title);
-        builder.setMessage(getActivity().getResources().getString(R.string.sim_change_data_message,
-                    currentSir.getDisplayName(), previousName));
-
-        builder.setPositiveButton(R.string.okay, new DialogInterface.OnClickListener() {
-            @Override
-            public void onClick(DialogInterface dialog, int id) {
-                mSubscriptionManager.setDefaultDataSubId(currentSir.getSubscriptionId());
-                setMobileDataEnabled(currentSir.getSubscriptionId(), true);
-                disableDataForOtherSubscriptions(currentSir);
-                updateBody();
-            }
-        });
-        builder.setNegativeButton(R.string.cancel, null);
-
-        builder.create().show();
-    }
-
-    private View.OnClickListener mDisableAtLimitListener = new View.OnClickListener() {
-        @Override
-        public void onClick(View v) {
-            final boolean disableAtLimit = !mDisableAtLimit.isChecked();
-            if (disableAtLimit) {
-                // enabling limit; show confirmation dialog which eventually
-                // calls setPolicyLimitBytes() once user confirms.
-                ConfirmLimitFragment.show(DataUsageSummary.this);
-            } else {
-                setPolicyLimitBytes(LIMIT_DISABLED);
-            }
-        }
-    };
-
-    private View.OnClickListener mAppRestrictListener = new View.OnClickListener() {
-        @Override
-        public void onClick(View v) {
-            final boolean restrictBackground = !mAppRestrict.isChecked();
-
-            if (restrictBackground) {
-                // enabling restriction; show confirmation dialog which
-                // eventually calls setRestrictBackground() once user
-                // confirms.
-                ConfirmAppRestrictFragment.show(DataUsageSummary.this);
-            } else {
-                setAppRestrictBackground(false);
-            }
-        }
-    };
-
-    private OnItemClickListener mListListener = new OnItemClickListener() {
-        @Override
-        public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
-            final Context context = view.getContext();
-            final AppItem app = (AppItem) parent.getItemAtPosition(position);
-
-            // TODO: sigh, remove this hack once we understand 6450986
-            if (mUidDetailProvider == null || app == null) return;
-
-            final UidDetail detail = mUidDetailProvider.getUidDetail(app.key, true);
-            AppDetailsFragment.show(DataUsageSummary.this, app, detail.label);
-        }
-    };
-
-    private OnItemSelectedListener mCycleListener = new OnItemSelectedListener() {
-        @Override
-        public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
-            final CycleItem cycle = (CycleItem) parent.getItemAtPosition(position);
-            if (cycle instanceof CycleChangeItem) {
-                // show cycle editor; will eventually call setPolicyCycleDay()
-                // when user finishes editing.
-                CycleEditorFragment.show(DataUsageSummary.this);
-
-                // reset spinner to something other than "change cycle..."
-                mCycleSpinner.setSelection(0);
-
-            } else {
-                if (LOGD) {
-                    Log.d(TAG, "showing cycle " + cycle + ", start=" + cycle.start + ", end="
-                            + cycle.end + "]");
-                }
-
-                // update chart to show selected cycle, and update detail data
-                // to match updated sweep bounds.
-                mChart.setVisibleRange(cycle.start, cycle.end);
-
-                updateDetailData();
-            }
-        }
-
-        @Override
-        public void onNothingSelected(AdapterView<?> parent) {
-            // ignored
-        }
-    };
-
-    /**
-     * Update details based on {@link #mChart} inspection range depending on
-     * current mode. In network mode, updates {@link #mAdapter} with sorted list
-     * of applications data usage, and when {@link #isAppDetailMode()} update
-     * app details.
-     */
-    private void updateDetailData() {
-        if (LOGD) Log.d(TAG, "updateDetailData()");
-
-        final long start = mChart.getInspectStart();
-        final long end = mChart.getInspectEnd();
-        final long now = System.currentTimeMillis();
-
-        final Context context = getActivity();
-
-        NetworkStatsHistory.Entry entry = null;
-        if (isAppDetailMode() && mChartData != null && mChartData.detail != null) {
-            // bind foreground/background to piechart and labels
-            entry = mChartData.detailDefault.getValues(start, end, now, entry);
-            final long defaultBytes = entry.rxBytes + entry.txBytes;
-            entry = mChartData.detailForeground.getValues(start, end, now, entry);
-            final long foregroundBytes = entry.rxBytes + entry.txBytes;
-            final long totalBytes = defaultBytes + foregroundBytes;
-
-            if (mAppTotal != null) {
-                mAppTotal.setText(Formatter.formatFileSize(context, totalBytes));
-            }
-            mAppBackground.setText(Formatter.formatFileSize(context, defaultBytes));
-            mAppForeground.setText(Formatter.formatFileSize(context, foregroundBytes));
-
-            // and finally leave with summary data for label below
-            entry = mChartData.detail.getValues(start, end, now, null);
-
-            getLoaderManager().destroyLoader(LOADER_SUMMARY);
-
-            mCycleSummary.setVisibility(View.GONE);
-
-        } else {
-            if (mChartData != null) {
-                entry = mChartData.network.getValues(start, end, now, null);
-            }
-
-            mCycleSummary.setVisibility(View.VISIBLE);
-
-            // kick off loader for detailed stats
-            getLoaderManager().restartLoader(LOADER_SUMMARY,
-                    SummaryForAllUidLoader.buildArgs(mTemplate, start, end), mSummaryCallbacks);
-        }
-
-        final long totalBytes = entry != null ? entry.rxBytes + entry.txBytes : 0;
-        final String totalPhrase = Formatter.formatFileSize(context, totalBytes);
-        mCycleSummary.setText(totalPhrase);
-
-        if (isMobileTab(mCurrentTab) || TAB_3G.equals(mCurrentTab)
-                || TAB_4G.equals(mCurrentTab)) {
-            if (isAppDetailMode()) {
-                mDisclaimer.setVisibility(View.GONE);
-            } else {
-                mDisclaimer.setVisibility(View.VISIBLE);
-            }
-        } else {
-            mDisclaimer.setVisibility(View.GONE);
-        }
-
-        // initial layout is finished above, ensure we have transitions
-        ensureLayoutTransitions();
-    }
-
-    private final LoaderCallbacks<ChartData> mChartDataCallbacks = new LoaderCallbacks<
-            ChartData>() {
-        @Override
-        public Loader<ChartData> onCreateLoader(int id, Bundle args) {
-            return new ChartDataLoader(getActivity(), mStatsSession, args);
-        }
-
-        @Override
-        public void onLoadFinished(Loader<ChartData> loader, ChartData data) {
-            mChartData = data;
-            mChart.bindNetworkStats(mChartData.network);
-            mChart.bindDetailNetworkStats(mChartData.detail);
-
-            // calcuate policy cycles based on available data
-            updatePolicy(true);
-            updateAppDetail();
-
-            // force scroll to top of body when showing detail
-            if (mChartData.detail != null) {
-                mListView.smoothScrollToPosition(0);
-            }
-        }
-
-        @Override
-        public void onLoaderReset(Loader<ChartData> loader) {
-            mChartData = null;
-            mChart.bindNetworkStats(null);
-            mChart.bindDetailNetworkStats(null);
-        }
-    };
-
-    private final LoaderCallbacks<NetworkStats> mSummaryCallbacks = new LoaderCallbacks<
-            NetworkStats>() {
-        @Override
-        public Loader<NetworkStats> onCreateLoader(int id, Bundle args) {
-            return new SummaryForAllUidLoader(getActivity(), mStatsSession, args);
-        }
-
-        @Override
-        public void onLoadFinished(Loader<NetworkStats> loader, NetworkStats data) {
-            final int[] restrictedUids = mPolicyManager.getUidsWithPolicy(
-                    POLICY_REJECT_METERED_BACKGROUND);
-            mAdapter.bindStats(data, restrictedUids);
-            updateEmptyVisible();
-        }
-
-        @Override
-        public void onLoaderReset(Loader<NetworkStats> loader) {
-            mAdapter.bindStats(null, new int[0]);
-            updateEmptyVisible();
-        }
-
-        private void updateEmptyVisible() {
-            final boolean isEmpty = mAdapter.isEmpty() && !isAppDetailMode();
-            mEmpty.setVisibility(isEmpty ? View.VISIBLE : View.GONE);
-            mStupidPadding.setVisibility(isEmpty ? View.VISIBLE : View.GONE);
-        }
-    };
-
-    private static String getActiveSubscriberId(Context context) {
-        final TelephonyManager tele = TelephonyManager.from(context);
-        final String actualSubscriberId = tele.getSubscriberId();
-        String retVal = SystemProperties.get(TEST_SUBSCRIBER_PROP, actualSubscriberId);
-        if (LOGD) Log.d(TAG, "getActiveSubscriberId=" + retVal + " actualSubscriberId=" + actualSubscriberId);
-        return retVal;
-    }
-
-    private static String getActiveSubscriberId(Context context, int subId) {
-        final TelephonyManager tele = TelephonyManager.from(context);
-        String retVal = tele.getSubscriberId(subId);
-        if (LOGD) Log.d(TAG, "getActiveSubscriberId=" + retVal + " subId=" + subId);
-        return retVal;
-    }
-
-    private DataUsageChartListener mChartListener = new DataUsageChartListener() {
-        @Override
-        public void onWarningChanged() {
-            setPolicyWarningBytes(mChart.getWarningBytes());
-        }
-
-        @Override
-        public void onLimitChanged() {
-            setPolicyLimitBytes(mChart.getLimitBytes());
-            updateBody();
-        }
-
-        @Override
-        public void requestWarningEdit() {
-            WarningEditorFragment.show(DataUsageSummary.this);
-        }
-
-        @Override
-        public void requestLimitEdit() {
-            LimitEditorFragment.show(DataUsageSummary.this);
-        }
-    };
-
-    /**
-     * List item that reflects a specific data usage cycle.
-     */
-    public static class CycleItem implements Comparable<CycleItem> {
-        public CharSequence label;
-        public long start;
-        public long end;
-
-        CycleItem(CharSequence label) {
-            this.label = label;
-        }
-
-        public CycleItem(Context context, long start, long end) {
-            this.label = formatDateRange(context, start, end);
-            this.start = start;
-            this.end = end;
-        }
-
-        @Override
-        public String toString() {
-            return label.toString();
-        }
-
-        @Override
-        public boolean equals(Object o) {
-            if (o instanceof CycleItem) {
-                final CycleItem another = (CycleItem) o;
-                return start == another.start && end == another.end;
-            }
-            return false;
-        }
-
-        @Override
-        public int compareTo(CycleItem another) {
-            return Long.compare(start, another.start);
-        }
-    }
-
-    private static final StringBuilder sBuilder = new StringBuilder(50);
-    private static final java.util.Formatter sFormatter = new java.util.Formatter(
-            sBuilder, Locale.getDefault());
-
-    public static String formatDateRange(Context context, long start, long end) {
-        final int flags = FORMAT_SHOW_DATE | FORMAT_ABBREV_MONTH;
-
-        synchronized (sBuilder) {
-            sBuilder.setLength(0);
-            return DateUtils.formatDateRange(context, sFormatter, start, end, flags, null)
-                    .toString();
-        }
-    }
-
-    /**
-     * Special-case data usage cycle that triggers dialog to change
-     * {@link NetworkPolicy#cycleDay}.
-     */
-    public static class CycleChangeItem extends CycleItem {
-        public CycleChangeItem(Context context) {
-            super(context.getString(R.string.data_usage_change_cycle));
-        }
-    }
-
-    public static class CycleAdapter extends ArrayAdapter<CycleItem> {
-        private boolean mChangePossible = false;
-        private boolean mChangeVisible = false;
-
-        private final CycleChangeItem mChangeItem;
-
-        public CycleAdapter(Context context) {
-            super(context, R.layout.data_usage_cycle_item);
-            setDropDownViewResource(R.layout.data_usage_cycle_item_dropdown);
-            mChangeItem = new CycleChangeItem(context);
-        }
-
-        public void setChangePossible(boolean possible) {
-            mChangePossible = possible;
-            updateChange();
-        }
-
-        public void setChangeVisible(boolean visible) {
-            mChangeVisible = visible;
-            updateChange();
-        }
-
-        private void updateChange() {
-            remove(mChangeItem);
-            if (mChangePossible && mChangeVisible) {
-                add(mChangeItem);
-            }
-        }
-
-        /**
-         * Find position of {@link CycleItem} in this adapter which is nearest
-         * the given {@link CycleItem}.
-         */
-        public int findNearestPosition(CycleItem target) {
-            if (target != null) {
-                final int count = getCount();
-                for (int i = count - 1; i >= 0; i--) {
-                    final CycleItem item = getItem(i);
-                    if (item instanceof CycleChangeItem) {
-                        continue;
-                    } else if (item.compareTo(target) >= 0) {
-                        return i;
-                    }
-                }
-            }
-            return 0;
-        }
-    }
-
-    /**
-     * Adapter of applications, sorted by total usage descending.
-     */
-    public static class DataUsageAdapter extends BaseAdapter {
-        private final UidDetailProvider mProvider;
-        private final int mInsetSide;
-        private final UserManager mUm;
-
-        private ArrayList<AppItem> mItems = Lists.newArrayList();
-        private long mLargest;
-
-        public DataUsageAdapter(final UserManager userManager, UidDetailProvider provider, int insetSide) {
-            mProvider = checkNotNull(provider);
-            mInsetSide = insetSide;
-            mUm = userManager;
-        }
-
-        /**
-         * Bind the given {@link NetworkStats}, or {@code null} to clear list.
-         */
-        public void bindStats(NetworkStats stats, int[] restrictedUids) {
-            mItems.clear();
-            mLargest = 0;
-
-            final int currentUserId = ActivityManager.getCurrentUser();
-            final List<UserHandle> profiles = mUm.getUserProfiles();
-            final SparseArray<AppItem> knownItems = new SparseArray<AppItem>();
-
-            NetworkStats.Entry entry = null;
-            final int size = stats != null ? stats.size() : 0;
-            for (int i = 0; i < size; i++) {
-                entry = stats.getValues(i, entry);
-
-                // Decide how to collapse items together
-                final int uid = entry.uid;
-
-                final int collapseKey;
-                final int category;
-                final int userId = UserHandle.getUserId(uid);
-                if (UserHandle.isApp(uid)) {
-                    if (profiles.contains(new UserHandle(userId))) {
-                        if (userId != currentUserId) {
-                            // Add to a managed user item.
-                            final int managedKey = UidDetailProvider.buildKeyForUser(userId);
-                            accumulate(managedKey, knownItems, entry,
-                                    AppItem.CATEGORY_USER);
-                        }
-                        // Add to app item.
-                        collapseKey = uid;
-                        category = AppItem.CATEGORY_APP;
-                    } else {
-                        // If it is a removed user add it to the removed users' key
-                        final UserInfo info = mUm.getUserInfo(userId);
-                        if (info == null) {
-                            collapseKey = UID_REMOVED;
-                            category = AppItem.CATEGORY_APP;
-                        } else {
-                            // Add to other user item.
-                            collapseKey = UidDetailProvider.buildKeyForUser(userId);
-                            category = AppItem.CATEGORY_USER;
-                        }
-                    }
-                } else if (uid == UID_REMOVED || uid == UID_TETHERING) {
-                    collapseKey = uid;
-                    category = AppItem.CATEGORY_APP;
-                } else {
-                    collapseKey = android.os.Process.SYSTEM_UID;
-                    category = AppItem.CATEGORY_APP;
-                }
-                accumulate(collapseKey, knownItems, entry, category);
-            }
-
-            final int restrictedUidsMax = restrictedUids.length;
-            for (int i = 0; i < restrictedUidsMax; ++i) {
-                final int uid = restrictedUids[i];
-                // Only splice in restricted state for current user or managed users
-                if (!profiles.contains(new UserHandle(UserHandle.getUserId(uid)))) {
-                    continue;
-                }
-
-                AppItem item = knownItems.get(uid);
-                if (item == null) {
-                    item = new AppItem(uid);
-                    item.total = -1;
-                    mItems.add(item);
-                    knownItems.put(item.key, item);
-                }
-                item.restricted = true;
-            }
-
-            if (!mItems.isEmpty()) {
-                final AppItem title = new AppItem();
-                title.category = AppItem.CATEGORY_APP_TITLE;
-                mItems.add(title);
-            }
-
-            Collections.sort(mItems);
-            notifyDataSetChanged();
-        }
-
-        /**
-         * Accumulate data usage of a network stats entry for the item mapped by the collapse key.
-         * Creates the item if needed.
-         *
-         * @param collapseKey the collapse key used to map the item.
-         * @param knownItems collection of known (already existing) items.
-         * @param entry the network stats entry to extract data usage from.
-         * @param itemCategory the item is categorized on the list view by this category. Must be
-         *            either AppItem.APP_ITEM_CATEGORY or AppItem.MANAGED_USER_ITEM_CATEGORY
-         */
-        private void accumulate(int collapseKey, final SparseArray<AppItem> knownItems,
-                NetworkStats.Entry entry, int itemCategory) {
-            final int uid = entry.uid;
-            AppItem item = knownItems.get(collapseKey);
-            if (item == null) {
-                item = new AppItem(collapseKey);
-                item.category = itemCategory;
-                mItems.add(item);
-                knownItems.put(item.key, item);
-            }
-            item.addUid(uid);
-            item.total += entry.rxBytes + entry.txBytes;
-            if (mLargest < item.total) {
-                mLargest = item.total;
-            }
-        }
-
-        @Override
-        public int getCount() {
-            return mItems.size();
-        }
-
-        @Override
-        public Object getItem(int position) {
-            return mItems.get(position);
-        }
-
-        @Override
-        public long getItemId(int position) {
-            return mItems.get(position).key;
-        }
-
-        /**
-         * See {@link #getItemViewType} for the view types.
-         */
-        @Override
-        public int getViewTypeCount() {
-            return 2;
-        }
-
-        /**
-         * Returns 1 for separator items and 0 for anything else.
-         */
-        @Override
-        public int getItemViewType(int position) {
-            final AppItem item = mItems.get(position);
-            if (item.category == AppItem.CATEGORY_APP_TITLE) {
-                return 1;
-            } else {
-                return 0;
-            }
-        }
-
-        @Override
-        public boolean areAllItemsEnabled() {
-            return false;
-        }
-
-        @Override
-        public boolean isEnabled(int position) {
-            if (position > mItems.size()) {
-                throw new ArrayIndexOutOfBoundsException();
-            }
-            return getItemViewType(position) == 0;
-        }
-
-        @Override
-        public View getView(int position, View convertView, ViewGroup parent) {
-            final AppItem item = mItems.get(position);
-            LayoutInflater inflater = LayoutInflater.from(parent.getContext());
-            if (getItemViewType(position) == 1) {
-                if (convertView == null) {
-                    convertView = Utils.inflateCategoryHeader(inflater, parent);
-                }
-
-                final TextView title = (TextView) convertView.findViewById(android.R.id.title);
-                title.setText(R.string.data_usage_app);
-
-            } else {
-                if (convertView == null) {
-                    convertView = inflater.inflate(R.layout.data_usage_item, parent, false);
-                    inflater.inflate(R.layout.widget_progress_bar,
-                            (ViewGroup) convertView.findViewById(android.R.id.widget_frame));
-
-                    if (mInsetSide > 0) {
-                        convertView.setPaddingRelative(mInsetSide, 0, mInsetSide, 0);
-                    }
-                }
-
-                final Context context = parent.getContext();
-
-                final TextView summary = (TextView) convertView.findViewById(android.R.id.summary);
-                final ProgressBar progress = (ProgressBar) convertView.findViewById(
-                        android.R.id.progress);
-
-                // kick off async load of app details
-                UidDetailTask.bindView(mProvider, item, convertView);
-
-                if (item.restricted && item.total <= 0) {
-                    summary.setText(R.string.data_usage_app_restricted);
-                    progress.setVisibility(View.GONE);
-                } else {
-                    summary.setText(Formatter.formatFileSize(context, item.total));
-                    progress.setVisibility(View.VISIBLE);
-                }
-
-                final int percentTotal = mLargest != 0 ? (int) (item.total * 100 / mLargest) : 0;
-                progress.setProgress(percentTotal);
-            }
-
-            return convertView;
-        }
-    }
-
-    /**
-     * Empty {@link Fragment} that controls display of UID details in
-     * {@link DataUsageSummary}.
-     */
-    public static class AppDetailsFragment extends Fragment {
-        private static final String EXTRA_APP = "app";
-
-        public static void show(DataUsageSummary parent, AppItem app, CharSequence label) {
-            show(parent, app, label, true);
-        }
-
-        public static void show(DataUsageSummary parent, AppItem app, CharSequence label,
-                boolean addToBack) {
-            if (!parent.isAdded()) return;
-
-            final Bundle args = new Bundle();
-            args.putParcelable(EXTRA_APP, app);
-
-            final AppDetailsFragment fragment = new AppDetailsFragment();
-            fragment.setArguments(args);
-            fragment.setTargetFragment(parent, 0);
-            final FragmentTransaction ft = parent.getFragmentManager().beginTransaction();
-            ft.add(fragment, TAG_APP_DETAILS);
-            if (addToBack) {
-                ft.addToBackStack(TAG_APP_DETAILS);
-            }
-            ft.setBreadCrumbTitle(
-                    parent.getResources().getString(R.string.data_usage_app_summary_title));
-            ft.commitAllowingStateLoss();
-        }
-
-        @Override
-        public void onStart() {
-            super.onStart();
-            final DataUsageSummary target = (DataUsageSummary) getTargetFragment();
-            target.mCurrentApp = getArguments().getParcelable(EXTRA_APP);
-            target.updateBody();
-        }
-
-        @Override
-        public void onStop() {
-            super.onStop();
-            final DataUsageSummary target = (DataUsageSummary) getTargetFragment();
-            target.mCurrentApp = null;
-            target.updateBody();
-        }
-
-        @Override
-        public boolean onOptionsItemSelected(MenuItem item) {
-            switch (item.getItemId()) {
-            case android.R.id.home:
-                getFragmentManager().popBackStack();
-                return true;
-            }
-            return super.onOptionsItemSelected(item);
-        }
-    }
-
-    /**
-     * Dialog to request user confirmation before setting
-     * {@link NetworkPolicy#limitBytes}.
-     */
-    public static class ConfirmLimitFragment extends DialogFragment {
-        private static final String EXTRA_MESSAGE = "message";
-        private static final String EXTRA_LIMIT_BYTES = "limitBytes";
-
-        public static void show(DataUsageSummary parent) {
-            if (!parent.isAdded()) return;
-
-            final NetworkPolicy policy = parent.mPolicyEditor.getPolicy(parent.mTemplate);
-            if (policy == null) return;
-
-            final Resources res = parent.getResources();
-            final CharSequence message;
-            final long minLimitBytes = (long) (policy.warningBytes * 1.2f);
-            final long limitBytes;
-
-            // TODO: customize default limits based on network template
-            final String currentTab = parent.mCurrentTab;
-            if (TAB_3G.equals(currentTab)) {
-                message = res.getString(R.string.data_usage_limit_dialog_mobile);
-                limitBytes = Math.max(5 * GB_IN_BYTES, minLimitBytes);
-            } else if (TAB_4G.equals(currentTab)) {
-                message = res.getString(R.string.data_usage_limit_dialog_mobile);
-                limitBytes = Math.max(5 * GB_IN_BYTES, minLimitBytes);
-            } else if (isMobileTab(currentTab)) {
-                message = res.getString(R.string.data_usage_limit_dialog_mobile);
-                limitBytes = Math.max(5 * GB_IN_BYTES, minLimitBytes);
-            } else {
-                throw new IllegalArgumentException("unknown current tab: " + currentTab);
-            }
-
-            final Bundle args = new Bundle();
-            args.putCharSequence(EXTRA_MESSAGE, message);
-            args.putLong(EXTRA_LIMIT_BYTES, limitBytes);
-
-            final ConfirmLimitFragment dialog = new ConfirmLimitFragment();
-            dialog.setArguments(args);
-            dialog.setTargetFragment(parent, 0);
-            dialog.show(parent.getFragmentManager(), TAG_CONFIRM_LIMIT);
-        }
-
-        @Override
-        public Dialog onCreateDialog(Bundle savedInstanceState) {
-            final Context context = getActivity();
-
-            final CharSequence message = getArguments().getCharSequence(EXTRA_MESSAGE);
-            final long limitBytes = getArguments().getLong(EXTRA_LIMIT_BYTES);
-
-            final AlertDialog.Builder builder = new AlertDialog.Builder(context);
-            builder.setTitle(R.string.data_usage_limit_dialog_title);
-            builder.setMessage(message);
-
-            builder.setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() {
-                @Override
-                public void onClick(DialogInterface dialog, int which) {
-                    final DataUsageSummary target = (DataUsageSummary) getTargetFragment();
-                    if (target != null) {
-                        target.setPolicyLimitBytes(limitBytes);
-                    }
-                }
-            });
-
-            return builder.create();
-        }
-    }
-
-    /**
-     * Dialog to edit {@link NetworkPolicy#cycleDay}.
-     */
-    public static class CycleEditorFragment extends DialogFragment {
-        private static final String EXTRA_TEMPLATE = "template";
-
-        public static void show(DataUsageSummary parent) {
-            if (!parent.isAdded()) return;
-
-            final Bundle args = new Bundle();
-            args.putParcelable(EXTRA_TEMPLATE, parent.mTemplate);
-
-            final CycleEditorFragment dialog = new CycleEditorFragment();
-            dialog.setArguments(args);
-            dialog.setTargetFragment(parent, 0);
-            dialog.show(parent.getFragmentManager(), TAG_CYCLE_EDITOR);
-        }
-
-        @Override
-        public Dialog onCreateDialog(Bundle savedInstanceState) {
-            final Context context = getActivity();
-            final DataUsageSummary target = (DataUsageSummary) getTargetFragment();
-            final NetworkPolicyEditor editor = target.mPolicyEditor;
-
-            final AlertDialog.Builder builder = new AlertDialog.Builder(context);
-            final LayoutInflater dialogInflater = LayoutInflater.from(builder.getContext());
-
-            final View view = dialogInflater.inflate(R.layout.data_usage_cycle_editor, null, false);
-            final NumberPicker cycleDayPicker = (NumberPicker) view.findViewById(R.id.cycle_day);
-
-            final NetworkTemplate template = getArguments().getParcelable(EXTRA_TEMPLATE);
-            final int cycleDay = editor.getPolicyCycleDay(template);
-
-            cycleDayPicker.setMinValue(1);
-            cycleDayPicker.setMaxValue(31);
-            cycleDayPicker.setValue(cycleDay);
-            cycleDayPicker.setWrapSelectorWheel(true);
-
-            builder.setTitle(R.string.data_usage_cycle_editor_title);
-            builder.setView(view);
-
-            builder.setPositiveButton(R.string.data_usage_cycle_editor_positive,
-                    new DialogInterface.OnClickListener() {
-                        @Override
-                        public void onClick(DialogInterface dialog, int which) {
-                            // clear focus to finish pending text edits
-                            cycleDayPicker.clearFocus();
-
-                            final int cycleDay = cycleDayPicker.getValue();
-                            final String cycleTimezone = new Time().timezone;
-                            editor.setPolicyCycleDay(template, cycleDay, cycleTimezone);
-                            target.updatePolicy(true);
-                        }
-                    });
-
-            return builder.create();
-        }
-    }
-
-    /**
-     * Dialog to edit {@link NetworkPolicy#warningBytes}.
-     */
-    public static class WarningEditorFragment extends DialogFragment {
-        private static final String EXTRA_TEMPLATE = "template";
-
-        public static void show(DataUsageSummary parent) {
-            if (!parent.isAdded()) return;
-
-            final Bundle args = new Bundle();
-            args.putParcelable(EXTRA_TEMPLATE, parent.mTemplate);
-
-            final WarningEditorFragment dialog = new WarningEditorFragment();
-            dialog.setArguments(args);
-            dialog.setTargetFragment(parent, 0);
-            dialog.show(parent.getFragmentManager(), TAG_WARNING_EDITOR);
-        }
-
-        @Override
-        public Dialog onCreateDialog(Bundle savedInstanceState) {
-            final Context context = getActivity();
-            final DataUsageSummary target = (DataUsageSummary) getTargetFragment();
-            final NetworkPolicyEditor editor = target.mPolicyEditor;
-
-            final AlertDialog.Builder builder = new AlertDialog.Builder(context);
-            final LayoutInflater dialogInflater = LayoutInflater.from(builder.getContext());
-
-            final View view = dialogInflater.inflate(R.layout.data_usage_bytes_editor, null, false);
-            final NumberPicker bytesPicker = (NumberPicker) view.findViewById(R.id.bytes);
-
-            final NetworkTemplate template = getArguments().getParcelable(EXTRA_TEMPLATE);
-            final long warningBytes = editor.getPolicyWarningBytes(template);
-            final long limitBytes = editor.getPolicyLimitBytes(template);
-
-            bytesPicker.setMinValue(0);
-            if (limitBytes != LIMIT_DISABLED) {
-                bytesPicker.setMaxValue((int) (limitBytes / MB_IN_BYTES) - 1);
-            } else {
-                bytesPicker.setMaxValue(Integer.MAX_VALUE);
-            }
-            bytesPicker.setValue((int) (warningBytes / MB_IN_BYTES));
-            bytesPicker.setWrapSelectorWheel(false);
-
-            builder.setTitle(R.string.data_usage_warning_editor_title);
-            builder.setView(view);
-
-            builder.setPositiveButton(R.string.data_usage_cycle_editor_positive,
-                    new DialogInterface.OnClickListener() {
-                        @Override
-                        public void onClick(DialogInterface dialog, int which) {
-                            // clear focus to finish pending text edits
-                            bytesPicker.clearFocus();
-
-                            final long bytes = bytesPicker.getValue() * MB_IN_BYTES;
-                            editor.setPolicyWarningBytes(template, bytes);
-                            target.updatePolicy(false);
-                        }
-                    });
-
-            return builder.create();
-        }
-    }
-
-    /**
-     * Dialog to edit {@link NetworkPolicy#limitBytes}.
-     */
-    public static class LimitEditorFragment extends DialogFragment {
-        private static final String EXTRA_TEMPLATE = "template";
-
-        public static void show(DataUsageSummary parent) {
-            if (!parent.isAdded()) return;
-
-            final Bundle args = new Bundle();
-            args.putParcelable(EXTRA_TEMPLATE, parent.mTemplate);
-
-            final LimitEditorFragment dialog = new LimitEditorFragment();
-            dialog.setArguments(args);
-            dialog.setTargetFragment(parent, 0);
-            dialog.show(parent.getFragmentManager(), TAG_LIMIT_EDITOR);
-        }
-
-        @Override
-        public Dialog onCreateDialog(Bundle savedInstanceState) {
-            final Context context = getActivity();
-            final DataUsageSummary target = (DataUsageSummary) getTargetFragment();
-            final NetworkPolicyEditor editor = target.mPolicyEditor;
-
-            final AlertDialog.Builder builder = new AlertDialog.Builder(context);
-            final LayoutInflater dialogInflater = LayoutInflater.from(builder.getContext());
-
-            final View view = dialogInflater.inflate(R.layout.data_usage_bytes_editor, null, false);
-            final NumberPicker bytesPicker = (NumberPicker) view.findViewById(R.id.bytes);
-
-            final NetworkTemplate template = getArguments().getParcelable(EXTRA_TEMPLATE);
-            final long warningBytes = editor.getPolicyWarningBytes(template);
-            final long limitBytes = editor.getPolicyLimitBytes(template);
-
-            bytesPicker.setMaxValue(Integer.MAX_VALUE);
-            if (warningBytes != WARNING_DISABLED && limitBytes > 0) {
-                bytesPicker.setMinValue((int) (warningBytes / MB_IN_BYTES) + 1);
-            } else {
-                bytesPicker.setMinValue(0);
-            }
-            bytesPicker.setValue((int) (limitBytes / MB_IN_BYTES));
-            bytesPicker.setWrapSelectorWheel(false);
-
-            builder.setTitle(R.string.data_usage_limit_editor_title);
-            builder.setView(view);
-
-            builder.setPositiveButton(R.string.data_usage_cycle_editor_positive,
-                    new DialogInterface.OnClickListener() {
-                        @Override
-                        public void onClick(DialogInterface dialog, int which) {
-                            // clear focus to finish pending text edits
-                            bytesPicker.clearFocus();
-
-                            final long bytes = bytesPicker.getValue() * MB_IN_BYTES;
-                            editor.setPolicyLimitBytes(template, bytes);
-                            target.updatePolicy(false);
-                        }
-                    });
-
-            return builder.create();
-        }
-    }
-    /**
-     * Dialog to request user confirmation before disabling data.
-     */
-    public static class ConfirmDataDisableFragment extends DialogFragment {
-        static int mSubId;
-        public static void show(DataUsageSummary parent, int subId) {
-            mSubId = subId;
-            if (!parent.isAdded()) return;
-
-            final ConfirmDataDisableFragment dialog = new ConfirmDataDisableFragment();
-            dialog.setTargetFragment(parent, 0);
-            dialog.show(parent.getFragmentManager(), TAG_CONFIRM_DATA_DISABLE);
-        }
-
-        @Override
-        public Dialog onCreateDialog(Bundle savedInstanceState) {
-            final Context context = getActivity();
-
-            final AlertDialog.Builder builder = new AlertDialog.Builder(context);
-            builder.setMessage(R.string.data_usage_disable_mobile);
-
-            builder.setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() {
-                @Override
-                public void onClick(DialogInterface dialog, int which) {
-                    final DataUsageSummary target = (DataUsageSummary) getTargetFragment();
-                    if (target != null) {
-                        // TODO: extend to modify policy enabled flag.
-                        target.setMobileDataEnabled(mSubId, false);
-                    }
-                }
-            });
-            builder.setNegativeButton(android.R.string.cancel, null);
-
-            return builder.create();
-        }
-    }
-
-    /**
-     * Dialog to request user confirmation before setting
-     * {@link INetworkPolicyManager#setRestrictBackground(boolean)}.
-     */
-    public static class ConfirmRestrictFragment extends DialogFragment {
-        public static void show(DataUsageSummary parent) {
-            if (!parent.isAdded()) return;
-
-            final ConfirmRestrictFragment dialog = new ConfirmRestrictFragment();
-            dialog.setTargetFragment(parent, 0);
-            dialog.show(parent.getFragmentManager(), TAG_CONFIRM_RESTRICT);
-        }
-
-        @Override
-        public Dialog onCreateDialog(Bundle savedInstanceState) {
-            final Context context = getActivity();
-
-            final AlertDialog.Builder builder = new AlertDialog.Builder(context);
-            builder.setTitle(R.string.data_usage_restrict_background_title);
-            if (Utils.hasMultipleUsers(context)) {
-                builder.setMessage(R.string.data_usage_restrict_background_multiuser);
-            } else {
-                builder.setMessage(R.string.data_usage_restrict_background);
-            }
-
-            builder.setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() {
-                @Override
-                public void onClick(DialogInterface dialog, int which) {
-                    final DataUsageSummary target = (DataUsageSummary) getTargetFragment();
-                    if (target != null) {
-                        target.setRestrictBackground(true);
-                    }
-                }
-            });
-            builder.setNegativeButton(android.R.string.cancel, null);
-
-            return builder.create();
-        }
-    }
-
-    /**
-     * Dialog to inform user that {@link #POLICY_REJECT_METERED_BACKGROUND}
-     * change has been denied, usually based on
-     * {@link DataUsageSummary#hasLimitedNetworks()}.
-     */
-    public static class DeniedRestrictFragment extends DialogFragment {
-        public static void show(DataUsageSummary parent) {
-            if (!parent.isAdded()) return;
-
-            final DeniedRestrictFragment dialog = new DeniedRestrictFragment();
-            dialog.setTargetFragment(parent, 0);
-            dialog.show(parent.getFragmentManager(), TAG_DENIED_RESTRICT);
-        }
-
-        @Override
-        public Dialog onCreateDialog(Bundle savedInstanceState) {
-            final Context context = getActivity();
-
-            final AlertDialog.Builder builder = new AlertDialog.Builder(context);
-            builder.setTitle(R.string.data_usage_app_restrict_background);
-            builder.setMessage(R.string.data_usage_restrict_denied_dialog);
-            builder.setPositiveButton(android.R.string.ok, null);
-
-            return builder.create();
-        }
-    }
-
-    /**
-     * Dialog to request user confirmation before setting
-     * {@link #POLICY_REJECT_METERED_BACKGROUND}.
-     */
-    public static class ConfirmAppRestrictFragment extends DialogFragment {
-        public static void show(DataUsageSummary parent) {
-            if (!parent.isAdded()) return;
-
-            final ConfirmAppRestrictFragment dialog = new ConfirmAppRestrictFragment();
-            dialog.setTargetFragment(parent, 0);
-            dialog.show(parent.getFragmentManager(), TAG_CONFIRM_APP_RESTRICT);
-        }
-
-        @Override
-        public Dialog onCreateDialog(Bundle savedInstanceState) {
-            final Context context = getActivity();
-
-            final AlertDialog.Builder builder = new AlertDialog.Builder(context);
-            builder.setTitle(R.string.data_usage_app_restrict_dialog_title);
-            builder.setMessage(R.string.data_usage_app_restrict_dialog);
-
-            builder.setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() {
-                @Override
-                public void onClick(DialogInterface dialog, int which) {
-                    final DataUsageSummary target = (DataUsageSummary) getTargetFragment();
-                    if (target != null) {
-                        target.setAppRestrictBackground(true);
-                    }
-                }
-            });
-            builder.setNegativeButton(android.R.string.cancel, null);
-
-            return builder.create();
-        }
-    }
-
-    /**
-     * Compute default tab that should be selected, based on
-     * {@link NetworkPolicyManager#EXTRA_NETWORK_TEMPLATE} extra.
-     */
-    private static String computeTabFromIntent(Intent intent) {
-        final NetworkTemplate template = intent.getParcelableExtra(EXTRA_NETWORK_TEMPLATE);
-        if (template == null) {
-            final int subId = intent.getIntExtra(PhoneConstants.SUBSCRIPTION_KEY,
-                    SubscriptionManager.INVALID_SUBSCRIPTION_ID);
-            if (SubscriptionManager.isValidSubscriptionId(subId)) {
-                return TAB_MOBILE + String.valueOf(subId);
-            }
-            return null;
-        }
-
-        switch (template.getMatchRule()) {
-            case MATCH_MOBILE_3G_LOWER:
-                return TAB_3G;
-            case MATCH_MOBILE_4G:
-                return TAB_4G;
-            case MATCH_MOBILE_ALL:
-                return TAB_MOBILE;
-            case MATCH_WIFI:
-                return TAB_WIFI;
-            default:
-                return null;
-        }
-    }
-
-    /**
-     * Background task that loads {@link UidDetail}, binding to
-     * {@link DataUsageAdapter} row item when finished.
-     */
-    private static class UidDetailTask extends AsyncTask<Void, Void, UidDetail> {
-        private final UidDetailProvider mProvider;
-        private final AppItem mItem;
-        private final View mTarget;
-
-        private UidDetailTask(UidDetailProvider provider, AppItem item, View target) {
-            mProvider = checkNotNull(provider);
-            mItem = checkNotNull(item);
-            mTarget = checkNotNull(target);
-        }
-
-        public static void bindView(
-                UidDetailProvider provider, AppItem item, View target) {
-            final UidDetailTask existing = (UidDetailTask) target.getTag();
-            if (existing != null) {
-                existing.cancel(false);
-            }
-
-            final UidDetail cachedDetail = provider.getUidDetail(item.key, false);
-            if (cachedDetail != null) {
-                bindView(cachedDetail, target);
-            } else {
-                target.setTag(new UidDetailTask(provider, item, target).executeOnExecutor(
-                        AsyncTask.THREAD_POOL_EXECUTOR));
-            }
-        }
-
-        private static void bindView(UidDetail detail, View target) {
-            final ImageView icon = (ImageView) target.findViewById(android.R.id.icon);
-            final TextView title = (TextView) target.findViewById(android.R.id.title);
-
-            if (detail != null) {
-                icon.setImageDrawable(detail.icon);
-                title.setText(detail.label);
-                title.setContentDescription(detail.contentDescription);
-            } else {
-                icon.setImageDrawable(null);
-                title.setText(null);
-            }
-        }
-
-        @Override
-        protected void onPreExecute() {
-            bindView(null, mTarget);
-        }
-
-        @Override
-        protected UidDetail doInBackground(Void... params) {
-            return mProvider.getUidDetail(mItem.key, true);
-        }
-
-        @Override
-        protected void onPostExecute(UidDetail result) {
-            bindView(result, mTarget);
-        }
-    }
-
-    /**
-     * Test if device has a mobile data radio with SIM in ready state.
-     */
-    public static boolean hasReadyMobileRadio(Context context) {
-        if (TEST_RADIOS) {
-            return SystemProperties.get(TEST_RADIOS_PROP).contains("mobile");
-        }
-
-        final ConnectivityManager conn = ConnectivityManager.from(context);
-        final TelephonyManager tele = TelephonyManager.from(context);
-
-        final List<SubscriptionInfo> subInfoList =
-                SubscriptionManager.from(context).getActiveSubscriptionInfoList();
-        // No activated Subscriptions
-        if (subInfoList == null) {
-            if (LOGD) Log.d(TAG, "hasReadyMobileRadio: subInfoList=null");
-            return false;
-        }
-        // require both supported network and ready SIM
-        boolean isReady = true;
-        for (SubscriptionInfo subInfo : subInfoList) {
-            isReady = isReady & tele.getSimState(subInfo.getSimSlotIndex()) == SIM_STATE_READY;
-            if (LOGD) Log.d(TAG, "hasReadyMobileRadio: subInfo=" + subInfo);
-        }
-        boolean retVal = conn.isNetworkSupported(TYPE_MOBILE) && isReady;
-        if (LOGD) {
-            Log.d(TAG, "hasReadyMobileRadio:"
-                    + " conn.isNetworkSupported(TYPE_MOBILE)="
-                                            + conn.isNetworkSupported(TYPE_MOBILE)
-                    + " isReady=" + isReady);
-        }
-        return retVal;
-    }
-
-    /*
-     * TODO: consider adding to TelephonyManager or SubscritpionManager.
-     */
-    public static boolean hasReadyMobileRadio(Context context, int subId) {
-        if (TEST_RADIOS) {
-            return SystemProperties.get(TEST_RADIOS_PROP).contains("mobile");
-        }
-
-        final ConnectivityManager conn = ConnectivityManager.from(context);
-        final TelephonyManager tele = TelephonyManager.from(context);
-        final int slotId = SubscriptionManager.getSlotId(subId);
-        final boolean isReady = tele.getSimState(slotId) == SIM_STATE_READY;
-
-        boolean retVal =  conn.isNetworkSupported(TYPE_MOBILE) && isReady;
-        if (LOGD) Log.d(TAG, "hasReadyMobileRadio: subId=" + subId
-                + " conn.isNetworkSupported(TYPE_MOBILE)=" + conn.isNetworkSupported(TYPE_MOBILE)
-                + " isReady=" + isReady);
-        return retVal;
-    }
-
-    /**
-     * Test if device has a mobile 4G data radio.
-     */
-    public static boolean hasReadyMobile4gRadio(Context context) {
-        if (!NetworkPolicyEditor.ENABLE_SPLIT_POLICIES) {
-            return false;
-        }
-        if (TEST_RADIOS) {
-            return SystemProperties.get(TEST_RADIOS_PROP).contains("4g");
-        }
-
-        final ConnectivityManager conn = ConnectivityManager.from(context);
-        final TelephonyManager tele = TelephonyManager.from(context);
-
-        final boolean hasWimax = conn.isNetworkSupported(TYPE_WIMAX);
-        final boolean hasLte = (tele.getLteOnCdmaMode() == PhoneConstants.LTE_ON_CDMA_TRUE)
-                && hasReadyMobileRadio(context);
-        return hasWimax || hasLte;
-    }
-
-    /**
-     * Test if device has a Wi-Fi data radio.
-     */
-    public static boolean hasWifiRadio(Context context) {
-        if (TEST_RADIOS) {
-            return SystemProperties.get(TEST_RADIOS_PROP).contains("wifi");
-        }
-
-        final ConnectivityManager conn = ConnectivityManager.from(context);
-        return conn.isNetworkSupported(TYPE_WIFI);
-    }
-
-    /**
-     * Test if device has an ethernet network connection.
-     */
-    public boolean hasEthernet(Context context) {
-        if (TEST_RADIOS) {
-            return SystemProperties.get(TEST_RADIOS_PROP).contains("ethernet");
-        }
-
-        final ConnectivityManager conn = ConnectivityManager.from(context);
-        final boolean hasEthernet = conn.isNetworkSupported(TYPE_ETHERNET);
-
-        final long ethernetBytes;
-        if (mStatsSession != null) {
-            try {
-                ethernetBytes = mStatsSession.getSummaryForNetwork(
-                        NetworkTemplate.buildTemplateEthernet(), Long.MIN_VALUE, Long.MAX_VALUE)
-                        .getTotalBytes();
-            } catch (RemoteException e) {
-                throw new RuntimeException(e);
-            }
-        } else {
-            ethernetBytes = 0;
-        }
-
-        // only show ethernet when both hardware present and traffic has occurred
-        return hasEthernet && ethernetBytes > 0;
-    }
-
-    /**
-     * Inflate a {@link Preference} style layout, adding the given {@link View}
-     * widget into {@link android.R.id#widget_frame}.
-     */
-    private static View inflatePreference(LayoutInflater inflater, ViewGroup root, View widget) {
-        final View view = inflater.inflate(R.layout.preference, root, false);
-        final LinearLayout widgetFrame = (LinearLayout) view.findViewById(
-                android.R.id.widget_frame);
-        widgetFrame.addView(widget, new LinearLayout.LayoutParams(WRAP_CONTENT, WRAP_CONTENT));
-        return view;
-    }
-
-    /**
-     * Test if any networks are currently limited.
-     */
-    private boolean hasLimitedNetworks() {
-        return !buildLimitedNetworksList().isEmpty();
-    }
-
-    /**
-     * Build string describing currently limited networks, which defines when
-     * background data is restricted.
-     */
-    @Deprecated
-    private CharSequence buildLimitedNetworksString() {
-        final List<CharSequence> limited = buildLimitedNetworksList();
-
-        // handle case where no networks limited
-        if (limited.isEmpty()) {
-            limited.add(getText(R.string.data_usage_list_none));
-        }
-
-        final ICUResourceBundle icuBundle = (ICUResourceBundle) UResourceBundle.
-                getBundleInstance(ICUResourceBundle.ICU_BASE_NAME);
-        final String listMiddlePattern =
-                icuBundle.getStringWithFallback("listPattern/standard/middle");
-        // The returned pattern is something like "{0}, {1}", from which we want
-        // to extract the ", " part.
-        final int firstClosingBrace = listMiddlePattern.indexOf('}');
-        final int lastOpeningBrace = listMiddlePattern.lastIndexOf('{');
-        final CharSequence delimiter = listMiddlePattern.substring(
-                firstClosingBrace+1, lastOpeningBrace);
-
-        return TextUtils.join(delimiter, limited);
-    }
-
-    /**
-     * Build list of currently limited networks, which defines when background
-     * data is restricted.
-     */
-    @Deprecated
-    private List<CharSequence> buildLimitedNetworksList() {
-        final Context context = getActivity();
-
-        // build combined list of all limited networks
-        final ArrayList<CharSequence> limited = Lists.newArrayList();
-
-        final TelephonyManager tele = TelephonyManager.from(context);
-        if (tele.getSimState() == SIM_STATE_READY) {
-            final String subscriberId = getActiveSubscriberId(context);
-            if (mPolicyEditor.hasLimitedPolicy(buildTemplateMobileAll(subscriberId))) {
-                limited.add(getText(R.string.data_usage_list_mobile));
-            }
-            if (mPolicyEditor.hasLimitedPolicy(buildTemplateMobile3gLower(subscriberId))) {
-                limited.add(getText(R.string.data_usage_tab_3g));
-            }
-            if (mPolicyEditor.hasLimitedPolicy(buildTemplateMobile4g(subscriberId))) {
-                limited.add(getText(R.string.data_usage_tab_4g));
-            }
-        }
-
-        if (mPolicyEditor.hasLimitedPolicy(buildTemplateWifiWildcard())) {
-            limited.add(getText(R.string.data_usage_tab_wifi));
-        }
-        if (mPolicyEditor.hasLimitedPolicy(buildTemplateEthernet())) {
-            limited.add(getText(R.string.data_usage_tab_ethernet));
-        }
-
-        return limited;
-    }
-
-    /**
-     * Inset both selector and divider {@link Drawable} on the given
-     * {@link ListView} by the requested dimensions.
-     */
-    private static void insetListViewDrawables(ListView view, int insetSide) {
-        final Drawable selector = view.getSelector();
-        final Drawable divider = view.getDivider();
-
-        // fully unregister these drawables so callbacks can be maintained after
-        // wrapping below.
-        final Drawable stub = new ColorDrawable(Color.TRANSPARENT);
-        view.setSelector(stub);
-        view.setDivider(stub);
-
-        view.setSelector(new InsetBoundsDrawable(selector, insetSide));
-        view.setDivider(new InsetBoundsDrawable(divider, insetSide));
-    }
-
-    /**
-     * Set {@link android.R.id#title} for a preference view inflated with
-     * {@link #inflatePreference(LayoutInflater, ViewGroup, View)}.
-     */
-    private static void setPreferenceTitle(View parent, int resId) {
-        final TextView title = (TextView) parent.findViewById(android.R.id.title);
-        title.setText(resId);
-    }
-
-    /**
-     * Set {@link android.R.id#summary} for a preference view inflated with
-     * {@link #inflatePreference(LayoutInflater, ViewGroup, View)}.
-     */
-    private static void setPreferenceSummary(View parent, CharSequence string) {
-        final TextView summary = (TextView) parent.findViewById(android.R.id.summary);
-        summary.setVisibility(View.VISIBLE);
-        summary.setText(string);
-    }
-
-    private void addMobileTab(Context context, SubscriptionInfo subInfo, boolean isMultiSim) {
-        if (subInfo != null && mMobileTagMap != null) {
-            if (hasReadyMobileRadio(context, subInfo.getSubscriptionId())) {
-                if (isMultiSim) {
-                    mTabHost.addTab(buildTabSpec(mMobileTagMap.get(subInfo.getSubscriptionId()),
-                            subInfo.getDisplayName()));
-                } else {
-                    mTabHost.addTab(buildTabSpec(mMobileTagMap.get(subInfo.getSubscriptionId()),
-                            R.string.data_usage_tab_mobile));
-                }
-            }
-        } else {
-            if (LOGD) Log.d(TAG, "addMobileTab: subInfoList is null");
-        }
-    }
-
-    private SubscriptionInfo getCurrentTabSubInfo(Context context) {
-        if (mSubInfoList != null && mTabHost != null) {
-            final int currentTagIndex = mTabHost.getCurrentTab();
-            int i = 0;
-            for (SubscriptionInfo subInfo : mSubInfoList) {
-                if (hasReadyMobileRadio(context, subInfo.getSubscriptionId())) {
-                    if (i++ == currentTagIndex) {
-                        return subInfo;
-                    }
-                }
-            }
-        }
-        return null;
-    }
-
-    /**
-     * Init a map with subId key and mobile tag name
-     * @param subInfoList The subscription Info List
-     * @return The map or null if no activated subscription
-     */
-    private Map<Integer, String> initMobileTabTag(List<SubscriptionInfo> subInfoList) {
-        Map<Integer, String> map = null;
-        if (subInfoList != null) {
-            String mobileTag;
-            map = new HashMap<Integer, String>();
-            for (SubscriptionInfo subInfo : subInfoList) {
-                mobileTag = TAB_MOBILE + String.valueOf(subInfo.getSubscriptionId());
-                map.put(subInfo.getSubscriptionId(), mobileTag);
-            }
-        }
-        return map;
-    }
-
-    private static boolean isMobileTab(String currentTab) {
-        return currentTab != null ? currentTab.contains(TAB_MOBILE) : false;
-    }
-
-    private int getSubId(String currentTab) {
-        if (mMobileTagMap != null) {
-            Set<Integer> set = mMobileTagMap.keySet();
-            for (Integer subId : set) {
-                if (mMobileTagMap.get(subId).equals(currentTab)) {
-                    return subId;
-                }
-            }
-        }
-        Log.e(TAG, "currentTab = " + currentTab + " non mobile tab called this function");
-        return -1;
-    }
-
-    private boolean isMobileDataAvailable(int subId) {
-        return mSubscriptionManager.getActiveSubscriptionInfo(subId) != null;
-    }
-
-    private static class SummaryProvider
-            implements SummaryLoader.SummaryProvider {
-
-        private final Activity mActivity;
-        private final SummaryLoader mSummaryLoader;
-        private final MobileDataController mDataController;
-
-        public SummaryProvider(Activity activity, SummaryLoader summaryLoader) {
-            mActivity = activity;
-            mSummaryLoader = summaryLoader;
-            mDataController = new MobileDataController(activity);
-        }
-
-        @Override
-        public void setListening(boolean listening) {
-            if (listening) {
-                MobileDataController.DataUsageInfo info = mDataController.getDataUsageInfo();
-                String used;
-                if (info == null) {
-                    used = Formatter.formatFileSize(mActivity, 0);
-                } else if (info.limitLevel <= 0) {
-                    used = Formatter.formatFileSize(mActivity, info.usageLevel);
-                } else {
-                    used = Utils.formatPercentage(info.usageLevel, info.limitLevel);
-                }
-                mSummaryLoader.setSummary(this,
-                        mActivity.getString(R.string.data_usage_summary_format, used));
-            }
-        }
-    }
-
-    public static final SummaryLoader.SummaryProviderFactory SUMMARY_PROVIDER_FACTORY
-            = new SummaryLoader.SummaryProviderFactory() {
-        @Override
-        public SummaryLoader.SummaryProvider createSummaryProvider(Activity activity,
-                                                                   SummaryLoader summaryLoader) {
-            return new SummaryProvider(activity, summaryLoader);
-        }
-    };
-
-    /**
-     * For search
-     */
-    public static final SearchIndexProvider SEARCH_INDEX_DATA_PROVIDER =
-        new BaseSearchIndexProvider() {
-            @Override
-            public List<SearchIndexableRaw> getRawDataToIndex(Context context, boolean enabled) {
-                final List<SearchIndexableRaw> result = new ArrayList<SearchIndexableRaw>();
-
-                final Resources res = context.getResources();
-
-                // Add fragment title
-                SearchIndexableRaw data = new SearchIndexableRaw(context);
-                data.title = res.getString(R.string.data_usage_summary_title);
-                data.screenTitle = res.getString(R.string.data_usage_summary_title);
-                result.add(data);
-
-                // Mobile data
-                data = new SearchIndexableRaw(context);
-                data.key = DATA_USAGE_ENABLE_MOBILE_KEY;
-                data.title = res.getString(R.string.data_usage_enable_mobile);
-                data.screenTitle = res.getString(R.string.data_usage_summary_title);
-                result.add(data);
-
-                // Set mobile data limit
-                data = new SearchIndexableRaw(context);
-                data.key = DATA_USAGE_DISABLE_MOBILE_LIMIT_KEY;
-                data.title = res.getString(R.string.data_usage_disable_mobile_limit);
-                data.screenTitle = res.getString(R.string.data_usage_summary_title);
-                result.add(data);
-
-                // Data usage cycle
-                data = new SearchIndexableRaw(context);
-                data.key = DATA_USAGE_CYCLE_KEY;
-                data.title = res.getString(R.string.data_usage_cycle);
-                data.screenTitle = res.getString(R.string.data_usage_summary_title);
-                result.add(data);
-
-                return result;
-            }
-        };
-}
diff --git a/src/com/android/settings/HighlightingFragment.java b/src/com/android/settings/HighlightingFragment.java
deleted file mode 100644
index 2d305e7..0000000
--- a/src/com/android/settings/HighlightingFragment.java
+++ /dev/null
@@ -1,125 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.settings;
-
-import android.graphics.drawable.Drawable;
-import android.os.Bundle;
-import android.text.TextUtils;
-import android.view.View;
-import android.view.ViewGroup;
-
-public abstract class HighlightingFragment extends InstrumentedFragment {
-
-    private static final String TAG = "HighlightSettingsFragment";
-
-    private static final int DELAY_HIGHLIGHT_DURATION_MILLIS = 400;
-    private static final String SAVE_HIGHLIGHTED_KEY = "android:view_highlighted";
-
-    private String mViewKey;
-    private boolean mViewHighlighted = false;
-    private Drawable mHighlightDrawable;
-
-    @Override
-    public void onCreate(Bundle icicle) {
-        super.onCreate(icicle);
-
-        if (icicle != null) {
-            mViewHighlighted = icicle.getBoolean(SAVE_HIGHLIGHTED_KEY);
-        }
-    }
-
-    @Override
-    public void onSaveInstanceState(Bundle outState) {
-        super.onSaveInstanceState(outState);
-
-        outState.putBoolean(SAVE_HIGHLIGHTED_KEY, mViewHighlighted);
-    }
-
-    @Override
-    public void onActivityCreated(Bundle savedInstanceState) {
-        super.onActivityCreated(savedInstanceState);
-
-        final Bundle args = getArguments();
-        if (args != null) {
-            mViewKey = args.getString(SettingsActivity.EXTRA_FRAGMENT_ARG_KEY);
-            highlightViewIfNeeded();
-        }
-    }
-
-    public void highlightViewIfNeeded() {
-        if (!mViewHighlighted &&!TextUtils.isEmpty(mViewKey)) {
-            highlightView(mViewKey);
-        }
-    }
-
-    private Drawable getHighlightDrawable() {
-        if (mHighlightDrawable == null) {
-            mHighlightDrawable = getActivity().getDrawable(R.drawable.preference_highlight);
-        }
-        return mHighlightDrawable;
-    }
-
-    private void highlightView(String key) {
-        final Drawable highlight = getHighlightDrawable();
-
-        // Try locating the View thru its Tag / Key
-        final View view = findViewForKey(getView(), key);
-        if (view != null ) {
-            view.setBackground(highlight);
-
-            getView().postDelayed(new Runnable() {
-                @Override
-                public void run() {
-                    final int centerX = view.getWidth() / 2;
-                    final int centerY = view.getHeight() / 2;
-                    highlight.setHotspot(centerX, centerY);
-                    view.setPressed(true);
-                    view.setPressed(false);
-                }
-            }, DELAY_HIGHLIGHT_DURATION_MILLIS);
-
-            mViewHighlighted = true;
-        }
-    }
-
-    private View findViewForKey(View root, String key) {
-        if (checkTag(root, key)) {
-            return root;
-        }
-        if (root instanceof ViewGroup) {
-            final ViewGroup group = (ViewGroup) root;
-            final int count = group.getChildCount();
-            for (int n = 0; n < count; n++) {
-                final View child = group.getChildAt(n);
-                final View view = findViewForKey(child, key);
-                if (view != null) {
-                    return view;
-                }
-            }
-        }
-        return null;
-    }
-
-    private boolean checkTag(View view, String key) {
-        final Object tag = view.getTag(R.id.preference_highlight_key);
-        if (tag == null || !(tag instanceof String)) {
-            return false;
-        }
-        final String viewKey = (String) tag;
-        return (!TextUtils.isEmpty(viewKey) && viewKey.equals(key));
-    }
-}
diff --git a/src/com/android/settings/InstrumentedFragment.java b/src/com/android/settings/InstrumentedFragment.java
index 2d39961..58b8eb0 100644
--- a/src/com/android/settings/InstrumentedFragment.java
+++ b/src/com/android/settings/InstrumentedFragment.java
@@ -34,6 +34,9 @@
     public static final int CONFIGURE_WIFI = UNDECLARED + 4;
     public static final int DISPLAY_SCREEN_ZOOM = UNDECLARED + 5;
     public static final int ACCESSIBILITY_FONT_SIZE = UNDECLARED + 6;
+    public static final int DATA_USAGE_LIST = UNDECLARED + 7;
+    public static final int BILLING_CYCLE = UNDECLARED + 8;
+    public static final int APP_DATA_USAGE = UNDECLARED + 9;
 
     /**
      * Declare the view of this category.
diff --git a/src/com/android/settings/SettingsActivity.java b/src/com/android/settings/SettingsActivity.java
index de66aea..dab2cb7 100644
--- a/src/com/android/settings/SettingsActivity.java
+++ b/src/com/android/settings/SettingsActivity.java
@@ -68,6 +68,7 @@
 import com.android.settings.bluetooth.BluetoothSettings;
 import com.android.settings.dashboard.DashboardSummary;
 import com.android.settings.dashboard.SearchResultsSummary;
+import com.android.settings.datausage.DataUsageSummary;
 import com.android.settings.deviceinfo.PrivateVolumeForget;
 import com.android.settings.deviceinfo.PrivateVolumeSettings;
 import com.android.settings.deviceinfo.PublicVolumeSettings;
@@ -85,9 +86,9 @@
 import com.android.settings.notification.AppNotificationSettings;
 import com.android.settings.notification.ConfigureNotificationSettings;
 import com.android.settings.notification.NotificationAccessSettings;
-import com.android.settings.notification.SoundSettings;
 import com.android.settings.notification.NotificationStation;
 import com.android.settings.notification.OtherSoundSettings;
+import com.android.settings.notification.SoundSettings;
 import com.android.settings.notification.ZenAccessSettings;
 import com.android.settings.notification.ZenModeAutomationSettings;
 import com.android.settings.notification.ZenModeEventRuleSettings;
diff --git a/src/com/android/settings/SettingsPreferenceFragment.java b/src/com/android/settings/SettingsPreferenceFragment.java
index f79def9..2ba49ce 100644
--- a/src/com/android/settings/SettingsPreferenceFragment.java
+++ b/src/com/android/settings/SettingsPreferenceFragment.java
@@ -24,7 +24,6 @@
 import android.content.Context;
 import android.content.DialogInterface;
 import android.content.pm.PackageManager;
-import android.graphics.drawable.Drawable;
 import android.os.Bundle;
 import android.support.v7.preference.Preference;
 import android.support.v7.preference.PreferenceGroup;
@@ -187,6 +186,11 @@
         setEmptyView(loading);
     }
 
+    public void setLoading(boolean loading, boolean animate) {
+        View loading_container = getView().findViewById(R.id.loading_container);
+        Utils.handleLoadingContainer(loading_container, getListView(), !loading, animate);
+    }
+
     public void registerObserverIfNeeded() {
         if (!mIsDataSetObserverRegistered) {
             if (mCurrentRootAdapter != null) {
diff --git a/src/com/android/settings/SummaryPreference.java b/src/com/android/settings/SummaryPreference.java
new file mode 100644
index 0000000..0943a2b
--- /dev/null
+++ b/src/com/android/settings/SummaryPreference.java
@@ -0,0 +1,99 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the
+ * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the specific language governing
+ * permissions and limitations under the License.
+ */
+
+package com.android.settings;
+
+import android.content.Context;
+import android.support.v7.preference.Preference;
+import android.support.v7.preference.PreferenceViewHolder;
+import android.text.TextUtils;
+import android.util.AttributeSet;
+import android.view.View;
+import android.widget.TextView;
+import com.android.settings.applications.LinearColorBar;
+
+/**
+ * Provides a summary of a setting page in a preference.  Such as memory or data usage.
+ */
+public class SummaryPreference extends Preference {
+
+    private static final String TAG = "SummaryPreference";
+    private String mAmount;
+    private String mUnits;
+
+    private int mLeft, mMiddle, mRight;
+    private float mLeftRatio, mMiddleRatio, mRightRatio;
+    private String mStartLabel;
+    private String mEndLabel;
+
+    public SummaryPreference(Context context, AttributeSet attrs) {
+        super(context, attrs);
+        setLayoutResource(R.layout.settings_summary_preference);
+        mLeft = context.getColor(R.color.summary_default_start);
+        mRight = context.getColor(R.color.summary_default_end);
+    }
+
+    public void setAmount(String amount) {
+        mAmount = amount;
+        if (mAmount != null && mUnits != null) {
+            setTitle(TextUtils.expandTemplate(getContext().getText(R.string.storage_size_large),
+                    mAmount, mUnits));
+        }
+    }
+
+    public void setUnits(String units) {
+        mUnits = units;
+        if (mAmount != null && mUnits != null) {
+            setTitle(TextUtils.expandTemplate(getContext().getText(R.string.storage_size_large),
+                    mAmount, mUnits));
+        }
+    }
+
+    public void setLabels(String start, String end) {
+        mStartLabel = start;
+        mEndLabel = end;
+        notifyChanged();
+    }
+
+    public void setRatios(float left, float middle, float right) {
+        mLeftRatio = left;
+        mMiddleRatio = middle;
+        mRightRatio = right;
+        notifyChanged();
+    }
+
+    public void setColors(int left, int middle, int right) {
+        mLeft = left;
+        mMiddle = middle;
+        mRight = right;
+        notifyChanged();
+    }
+
+    @Override
+    public void onBindViewHolder(PreferenceViewHolder holder) {
+        super.onBindViewHolder(holder);
+
+        LinearColorBar colorBar = (LinearColorBar) holder.itemView.findViewById(R.id.color_bar);
+        colorBar.setRatios(mLeftRatio, mMiddleRatio, mRightRatio);
+        colorBar.setColors(mLeft, mMiddle, mRight);
+
+        if (!TextUtils.isEmpty(mStartLabel) || !TextUtils.isEmpty(mEndLabel)) {
+            holder.findViewById(R.id.label_bar).setVisibility(View.VISIBLE);
+            ((TextView) holder.findViewById(android.R.id.text1)).setText(mStartLabel);
+            ((TextView) holder.findViewById(android.R.id.text2)).setText(mEndLabel);
+        } else {
+            holder.findViewById(R.id.label_bar).setVisibility(View.GONE);
+        }
+    }
+}
diff --git a/src/com/android/settings/Utils.java b/src/com/android/settings/Utils.java
index 4afa1d9..96d0a37 100644
--- a/src/com/android/settings/Utils.java
+++ b/src/com/android/settings/Utils.java
@@ -67,6 +67,7 @@
 import android.text.Spannable;
 import android.text.SpannableString;
 import android.text.TextUtils;
+import android.text.format.DateUtils;
 import android.text.style.TtsSpan;
 import android.util.ArraySet;
 import android.util.Log;
@@ -81,6 +82,7 @@
 import android.widget.ListView;
 import android.widget.TabWidget;
 import com.android.internal.util.UserIcons;
+import com.android.settings.datausage.DataUsageList;
 
 import java.io.IOException;
 import java.io.InputStream;
@@ -91,6 +93,8 @@
 import java.util.Locale;
 
 import static android.content.Intent.EXTRA_USER;
+import static android.text.format.DateUtils.FORMAT_ABBREV_MONTH;
+import static android.text.format.DateUtils.FORMAT_SHOW_DATE;
 
 public final class Utils extends com.android.settingslib.Utils {
 
@@ -978,5 +982,19 @@
         context.getTheme().resolveAttribute(attr, value, true);
         return value.resourceId;
     }
+
+    private static final StringBuilder sBuilder = new StringBuilder(50);
+    private static final java.util.Formatter sFormatter = new java.util.Formatter(
+            sBuilder, Locale.getDefault());
+
+    public static String formatDateRange(Context context, long start, long end) {
+        final int flags = FORMAT_SHOW_DATE | FORMAT_ABBREV_MONTH;
+
+        synchronized (sBuilder) {
+            sBuilder.setLength(0);
+            return DateUtils.formatDateRange(context, sFormatter, start, end, flags, null)
+                    .toString();
+        }
+    }
 }
 
diff --git a/src/com/android/settings/applications/InstalledAppDetails.java b/src/com/android/settings/applications/InstalledAppDetails.java
index 7d8a7f0..42d8e46 100755
--- a/src/com/android/settings/applications/InstalledAppDetails.java
+++ b/src/com/android/settings/applications/InstalledAppDetails.java
@@ -21,7 +21,6 @@
 import android.app.AlertDialog;
 import android.app.LoaderManager.LoaderCallbacks;
 import android.app.admin.DevicePolicyManager;
-import android.icu.text.ListFormatter;
 import android.content.ActivityNotFoundException;
 import android.content.BroadcastReceiver;
 import android.content.ComponentName;
@@ -37,6 +36,7 @@
 import android.content.pm.UserInfo;
 import android.content.res.Resources;
 import android.graphics.drawable.Drawable;
+import android.icu.text.ListFormatter;
 import android.net.INetworkStatsService;
 import android.net.INetworkStatsSession;
 import android.net.NetworkTemplate;
@@ -63,16 +63,17 @@
 import android.widget.Button;
 import android.widget.ImageView;
 import android.widget.TextView;
-
 import com.android.internal.logging.MetricsLogger;
 import com.android.internal.os.BatterySipper;
 import com.android.internal.os.BatteryStatsHelper;
 import com.android.settings.AppHeader;
-import com.android.settings.DataUsageSummary;
 import com.android.settings.R;
 import com.android.settings.SettingsActivity;
 import com.android.settings.Utils;
 import com.android.settings.applications.PermissionsSummaryHelper.PermissionsResultCallback;
+import com.android.settings.datausage.AppDataUsage;
+import com.android.settings.datausage.DataUsageList;
+import com.android.settings.datausage.DataUsageSummary;
 import com.android.settings.fuelgauge.BatteryEntry;
 import com.android.settings.fuelgauge.PowerUsageDetail;
 import com.android.settings.notification.AppNotificationSettings;
@@ -753,13 +754,7 @@
             ProcessStatsBase.launchMemoryDetail((SettingsActivity) getActivity(),
                     mStatsManager.getMemInfo(), mStats, false);
         } else if (preference == mDataPreference) {
-            Bundle args = new Bundle();
-            args.putString(DataUsageSummary.EXTRA_SHOW_APP_IMMEDIATE_PKG,
-                    mAppEntry.info.packageName);
-
-            SettingsActivity sa = (SettingsActivity) getActivity();
-            sa.startPreferencePanel(DataUsageSummary.class.getName(), args, -1,
-                    getString(R.string.app_data_usage), this, SUB_INFO_FRAGMENT);
+            startAppInfoFragment(AppDataUsage.class, getString(R.string.app_data_usage));
         } else if (preference == mBatteryPreference) {
             BatteryEntry entry = new BatteryEntry(getActivity(), null, mUserManager, mSipper);
             PowerUsageDetail.startBatteryDetailPage((SettingsActivity) getActivity(),
@@ -794,7 +789,7 @@
     }
 
     public static NetworkTemplate getTemplate(Context context) {
-        if (DataUsageSummary.hasReadyMobileRadio(context)) {
+        if (DataUsageList.hasReadyMobileRadio(context)) {
             return NetworkTemplate.buildTemplateMobileWildcard();
         }
         if (DataUsageSummary.hasWifiRadio(context)) {
diff --git a/src/com/android/settings/applications/ProcessStatsDetail.java b/src/com/android/settings/applications/ProcessStatsDetail.java
index 9db79bc..4ad480e 100644
--- a/src/com/android/settings/applications/ProcessStatsDetail.java
+++ b/src/com/android/settings/applications/ProcessStatsDetail.java
@@ -40,14 +40,13 @@
 import android.view.MenuInflater;
 import android.view.MenuItem;
 import android.view.View;
-import android.widget.TextView;
-
 import com.android.internal.logging.MetricsLogger;
 import com.android.settings.AppHeader;
 import com.android.settings.CancellablePreference;
 import com.android.settings.CancellablePreference.OnCancelListener;
 import com.android.settings.R;
 import com.android.settings.SettingsPreferenceFragment;
+import com.android.settings.SummaryPreference;
 import com.android.settings.applications.ProcStatsEntry.Service;
 
 import java.util.ArrayList;
@@ -87,8 +86,6 @@
     private long mTotalTime;
     private long mOnePercentTime;
 
-    private LinearColorBar mColorBar;
-
     private double mMaxMemoryUsage;
 
     private double mTotalScale;
@@ -177,20 +174,19 @@
         mProcGroup = (PreferenceCategory) findPreference(KEY_PROCS);
         fillProcessesSection();
 
-        LayoutPreference headerLayout = (LayoutPreference) findPreference(KEY_DETAILS_HEADER);
+        SummaryPreference summaryPreference = (SummaryPreference) findPreference(KEY_DETAILS_HEADER);
 
         // TODO: Find way to share this code with ProcessStatsPreference.
         boolean statsForeground = mApp.mRunWeight > mApp.mBgWeight;
         double avgRam = (statsForeground ? mApp.mRunWeight : mApp.mBgWeight) * mWeightToRam;
         float avgRatio = (float) (avgRam / mMaxMemoryUsage);
         float remainingRatio = 1 - avgRatio;
-        mColorBar = (LinearColorBar) headerLayout.findViewById(R.id.color_bar);
         Context context = getActivity();
-        mColorBar.setColors( context.getColor(R.color.memory_max_use), 0,
-                context.getColor(R.color.memory_remaining));
-        mColorBar.setRatios(avgRatio, 0, remainingRatio);
-        ((TextView) headerLayout.findViewById(R.id.memory_state)).setText(
-                Formatter.formatShortFileSize(getContext(), (long) avgRam));
+        summaryPreference.setRatios(avgRatio, 0, remainingRatio);
+        Formatter.BytesResult usedResult = Formatter.formatBytes(context.getResources(),
+                (long) avgRam, Formatter.FLAG_SHORTER);
+        summaryPreference.setAmount(usedResult.value);
+        summaryPreference.setUnits(usedResult.units);
 
         long duration = Math.max(mApp.mRunDuration, mApp.mBgDuration);
         CharSequence frequency = ProcStatsPackageEntry.getFrequency(duration
diff --git a/src/com/android/settings/applications/ProcessStatsSummary.java b/src/com/android/settings/applications/ProcessStatsSummary.java
index 09fea89..28917c8 100644
--- a/src/com/android/settings/applications/ProcessStatsSummary.java
+++ b/src/com/android/settings/applications/ProcessStatsSummary.java
@@ -16,18 +16,15 @@
 package com.android.settings.applications;
 
 import android.app.Activity;
-import android.content.BroadcastReceiver;
 import android.content.Context;
 import android.os.Bundle;
 import android.support.v7.preference.Preference;
 import android.support.v7.preference.Preference.OnPreferenceClickListener;
-import android.text.TextUtils;
 import android.text.format.Formatter;
 import android.text.format.Formatter.BytesResult;
-import android.widget.TextView;
-
 import com.android.internal.logging.MetricsLogger;
 import com.android.settings.R;
+import com.android.settings.SummaryPreference;
 import com.android.settings.Utils;
 import com.android.settings.applications.ProcStatsData.MemInfo;
 import com.android.settings.dashboard.SummaryLoader;
@@ -42,9 +39,7 @@
     private static final String KEY_FREE = "free";
     private static final String KEY_APP_LIST = "apps_list";
 
-    private LinearColorBar mColors;
-    private LayoutPreference mHeader;
-    private TextView mMemStatus;
+    private SummaryPreference mSummaryPref;
 
     private Preference mPerformance;
     private Preference mTotalMemory;
@@ -57,9 +52,10 @@
         super.onCreate(icicle);
 
         addPreferencesFromResource(R.xml.process_stats_summary);
-        mHeader = (LayoutPreference) findPreference(KEY_STATUS_HEADER);
-        mMemStatus = (TextView) mHeader.findViewById(R.id.memory_state);
-        mColors = (LinearColorBar) mHeader.findViewById(R.id.color_bar);
+        mSummaryPref = (SummaryPreference) findPreference(KEY_STATUS_HEADER);
+        int memColor = getContext().getColor(R.color.running_processes_apps_ram);
+        mSummaryPref.setColors(memColor, memColor,
+                getContext().getColor(R.color.running_processes_free_ram));
 
         mPerformance = findPreference(KEY_PERFORMANCE);
         mTotalMemory = findPreference(KEY_TOTAL_MEMORY);
@@ -72,8 +68,6 @@
     @Override
     public void refreshUi() {
         Context context = getContext();
-        int memColor = context.getColor(R.color.running_processes_apps_ram);
-        mColors.setColors(memColor, memColor, context.getColor(R.color.running_processes_free_ram));
 
         MemInfo memInfo = mStatsManager.getMemInfo();
 
@@ -92,10 +86,10 @@
         } else {
             memString = memStatesStr[memStatesStr.length - 1];
         }
-        mMemStatus.setText(TextUtils.expandTemplate(getText(R.string.storage_size_large),
-                usedResult.value, usedResult.units));
+        mSummaryPref.setAmount(usedResult.value);
+        mSummaryPref.setUnits(usedResult.units);
         float usedRatio = (float)(usedRam / (freeRam + usedRam));
-        mColors.setRatios(usedRatio, 0, 1 - usedRatio);
+        mSummaryPref.setRatios(usedRatio, 0, 1 - usedRatio);
 
         mPerformance.setSummary(memString);
         mTotalMemory.setSummary(totalString);
diff --git a/src/com/android/settings/datausage/AppDataUsage.java b/src/com/android/settings/datausage/AppDataUsage.java
new file mode 100644
index 0000000..9ca066f
--- /dev/null
+++ b/src/com/android/settings/datausage/AppDataUsage.java
@@ -0,0 +1,343 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the
+ * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the specific language governing
+ * permissions and limitations under the License.
+ */
+
+package com.android.settings.datausage;
+
+import com.android.settings.AppHeader;
+import com.android.settings.InstrumentedFragment;
+import com.android.settings.R;
+import com.android.settings.applications.AppInfoBase;
+import com.android.settingslib.AppItem;
+import com.android.settingslib.net.ChartData;
+import com.android.settingslib.net.ChartDataLoader;
+
+import android.app.LoaderManager;
+import android.content.Context;
+import android.content.Intent;
+import android.content.Loader;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageManager;
+import android.graphics.drawable.Drawable;
+import android.net.INetworkStatsSession;
+import android.net.NetworkPolicy;
+import android.net.NetworkStatsHistory;
+import android.net.NetworkTemplate;
+import android.net.TrafficStats;
+import android.os.AsyncTask;
+import android.os.Bundle;
+import android.os.RemoteException;
+import android.os.UserHandle;
+import android.support.v14.preference.SwitchPreference;
+import android.support.v7.preference.Preference;
+import android.support.v7.preference.PreferenceCategory;
+import android.text.format.Formatter;
+import android.util.ArraySet;
+import android.util.Log;
+import android.view.View;
+import android.widget.AdapterView;
+import android.widget.Spinner;
+
+import static android.net.NetworkPolicyManager.POLICY_NONE;
+import static android.net.NetworkPolicyManager.POLICY_REJECT_METERED_BACKGROUND;
+
+public class AppDataUsage extends DataUsageBase implements Preference.OnPreferenceChangeListener {
+
+    public static final String ARG_APP_ITEM = "app_item";
+    public static final String ARG_NETWORK_TEMPLATE = "network_template";
+
+    private static final String KEY_TOTAL_USAGE = "total_usage";
+    private static final String KEY_FOREGROUND_USAGE = "foreground_usage";
+    private static final String KEY_BACKGROUND_USAGE = "background_usage";
+    private static final String KEY_APP_SETTINGS = "app_settings";
+    private static final String KEY_RESTRICT_BACKGROUND = "restrict_background";
+    private static final String KEY_APP_LIST = "app_list";
+
+    private static final int LOADER_CHART_DATA = 2;
+
+    private final ArraySet<String> mPackages = new ArraySet<>();
+    private Preference mTotalUsage;
+    private Preference mForegroundUsage;
+    private Preference mBackgroundUsage;
+    private Preference mAppSettings;
+    private SwitchPreference mRestrictBackground;
+    private PreferenceCategory mAppList;
+
+    private Drawable mIcon;
+    private CharSequence mLabel;
+    private INetworkStatsSession mStatsSession;
+    private Spinner mCycleSpinner;
+    private CycleAdapter mCycleAdapter;
+
+    private long mStart;
+    private long mEnd;
+    private ChartData mChartData;
+    private NetworkTemplate mTemplate;
+    private NetworkPolicy mPolicy;
+    private AppItem mAppItem;
+    private Intent mAppSettingsIntent;
+
+    @Override
+    public void onCreate(Bundle icicle) {
+        super.onCreate(icicle);
+        final Bundle args = getArguments();
+
+        try {
+            mStatsSession = services.mStatsService.openSession();
+        } catch (RemoteException e) {
+            throw new RuntimeException(e);
+        }
+
+        mAppItem = (args != null) ? (AppItem) args.getParcelable(ARG_APP_ITEM) : null;
+        mTemplate = (args != null) ? (NetworkTemplate) args.getParcelable(ARG_NETWORK_TEMPLATE)
+                : null;
+        if (mTemplate == null) {
+            Context context = getContext();
+            mTemplate = DataUsageSummary.getDefaultTemplate(context,
+                    DataUsageSummary.getDefaultSubscriptionId(context));
+        }
+        if (mAppItem == null) {
+            int uid = (args != null) ? args.getInt(AppInfoBase.ARG_PACKAGE_UID, -1)
+                    : getActivity().getIntent().getIntExtra(AppInfoBase.ARG_PACKAGE_UID, -1);
+            if (uid == -1) {
+                // TODO: Log error.
+                getActivity().finish();
+            } else {
+                addUid(uid);
+                mAppItem = new AppItem(uid);
+                mAppItem.addUid(uid);
+            }
+        } else {
+            for (int i = 0; i < mAppItem.uids.size(); i++) {
+                addUid(mAppItem.uids.keyAt(i));
+            }
+        }
+        if (mPackages.size() != 0) {
+            PackageManager pm = getPackageManager();
+            try {
+                ApplicationInfo info = pm.getApplicationInfo(mPackages.valueAt(0), 0);
+                mIcon = info.loadIcon(pm);
+                mLabel = info.loadLabel(pm);
+            } catch (PackageManager.NameNotFoundException e) {
+            }
+        }
+        addPreferencesFromResource(R.xml.app_data_usage);
+
+        mTotalUsage = findPreference(KEY_TOTAL_USAGE);
+        mForegroundUsage = findPreference(KEY_FOREGROUND_USAGE);
+        mBackgroundUsage = findPreference(KEY_BACKGROUND_USAGE);
+
+        if (UserHandle.isApp(mAppItem.key)) {
+            mRestrictBackground = (SwitchPreference) findPreference(KEY_RESTRICT_BACKGROUND);
+            mRestrictBackground.setOnPreferenceChangeListener(this);
+            mAppSettings = findPreference(KEY_APP_SETTINGS);
+
+            mAppSettingsIntent = new Intent(Intent.ACTION_MANAGE_NETWORK_USAGE);
+            mAppSettingsIntent.addCategory(Intent.CATEGORY_DEFAULT);
+
+            PackageManager pm = getPackageManager();
+            boolean matchFound = false;
+            for (String packageName : mPackages) {
+                mAppSettingsIntent.setPackage(packageName);
+                if (pm.resolveActivity(mAppSettingsIntent, 0) != null) {
+                    matchFound = true;
+                    break;
+                }
+            }
+            if (!matchFound) {
+                removePreference(KEY_APP_SETTINGS);
+                mAppSettings = null;
+            }
+
+            if (mPackages.size() > 1) {
+                mAppList = (PreferenceCategory) findPreference(KEY_APP_LIST);
+                for (int i = 1 ; i < mPackages.size(); i++) {
+                    new AppPrefLoader().executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR,
+                            mPackages.valueAt(i));
+                }
+            } else {
+                removePreference(KEY_APP_LIST);
+            }
+        } else {
+            removePreference(KEY_APP_SETTINGS);
+            removePreference(KEY_RESTRICT_BACKGROUND);
+            removePreference(KEY_APP_LIST);
+        }
+    }
+
+    @Override
+    public void onDestroy() {
+        TrafficStats.closeQuietly(mStatsSession);
+        super.onDestroy();
+    }
+
+    @Override
+    public void onResume() {
+        super.onResume();
+        mPolicy = services.mPolicyEditor.getPolicy(mTemplate);
+        getLoaderManager().restartLoader(LOADER_CHART_DATA,
+                ChartDataLoader.buildArgs(mTemplate, mAppItem), mChartDataCallbacks);
+        updatePrefs();
+    }
+
+    @Override
+    public boolean onPreferenceChange(Preference preference, Object newValue) {
+        if (preference == mRestrictBackground) {
+            setAppRestrictBackground((Boolean) newValue);
+            return true;
+        }
+        return false;
+    }
+
+    @Override
+    public boolean onPreferenceTreeClick(Preference preference) {
+        if (preference == mAppSettings) {
+            // TODO: target towards entire UID instead of just first package
+            getActivity().startActivityAsUser(mAppSettingsIntent, new UserHandle(
+                    UserHandle.getUserId(mAppItem.key)));
+            return true;
+        }
+        return super.onPreferenceTreeClick(preference);
+    }
+
+    private void updatePrefs() {
+        if (mRestrictBackground != null) {
+            mRestrictBackground.setChecked(getAppRestrictBackground());
+        }
+    }
+
+    private void addUid(int uid) {
+        String[] packages = getPackageManager().getPackagesForUid(uid);
+        if (packages != null) {
+            for (int i = 0; i < packages.length; i++) {
+                mPackages.add(packages[i]);
+            }
+        }
+    }
+
+    private void bindData() {
+        if (mChartData == null || mStart == 0) {
+            return;
+        }
+        final Context context = getContext();
+        final long now = System.currentTimeMillis();
+
+        NetworkStatsHistory.Entry entry = null;
+        entry = mChartData.detailDefault.getValues(mStart, mEnd, now, entry);
+        final long backgroundBytes = entry.rxBytes + entry.txBytes;
+        entry = mChartData.detailForeground.getValues(mStart, mEnd, now, entry);
+        final long foregroundBytes = entry.rxBytes + entry.txBytes;
+        final long totalBytes = backgroundBytes + foregroundBytes;
+
+        mTotalUsage.setSummary(Formatter.formatFileSize(context, totalBytes));
+        mForegroundUsage.setSummary(Formatter.formatFileSize(context, foregroundBytes));
+        mBackgroundUsage.setSummary(Formatter.formatFileSize(context, backgroundBytes));
+    }
+
+    private boolean getAppRestrictBackground() {
+        final int uid = mAppItem.key;
+        final int uidPolicy = services.mPolicyManager.getUidPolicy(uid);
+        return (uidPolicy & POLICY_REJECT_METERED_BACKGROUND) != 0;
+    }
+
+    private void setAppRestrictBackground(boolean restrictBackground) {
+        final int uid = mAppItem.key;
+        services.mPolicyManager.setUidPolicy(
+                uid, restrictBackground ? POLICY_REJECT_METERED_BACKGROUND : POLICY_NONE);
+    }
+
+    @Override
+    public void onViewCreated(View view, Bundle savedInstanceState) {
+        super.onViewCreated(view, savedInstanceState);
+
+        View header = setPinnedHeaderView(R.layout.data_usage_app_header);
+        String pkg = mPackages.size() != 0 ? mPackages.valueAt(0) : null;
+        int uid = 0;
+        try {
+            uid = pkg != null ? getPackageManager().getPackageUid(pkg, 0) : 0;
+        } catch (PackageManager.NameNotFoundException e) {
+        }
+        AppHeader.setupHeaderView(getActivity(), mIcon, mLabel,
+                pkg, uid, AppHeader.includeAppInfo(this), 0, header);
+
+        mCycleSpinner = (Spinner) header.findViewById(R.id.filter_spinner);
+        mCycleAdapter = new CycleAdapter(getContext(), mCycleSpinner, mCycleListener);
+    }
+
+    @Override
+    protected int getMetricsCategory() {
+        return InstrumentedFragment.APP_DATA_USAGE;
+    }
+
+    private AdapterView.OnItemSelectedListener mCycleListener =
+            new AdapterView.OnItemSelectedListener() {
+        @Override
+        public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
+            final CycleAdapter.CycleItem cycle =
+                    (CycleAdapter.CycleItem) parent.getItemAtPosition(position);
+
+            mStart = cycle.start;
+            mEnd = cycle.end;
+            bindData();
+        }
+
+        @Override
+        public void onNothingSelected(AdapterView<?> parent) {
+            // ignored
+        }
+    };
+
+    private final LoaderManager.LoaderCallbacks<ChartData> mChartDataCallbacks =
+            new LoaderManager.LoaderCallbacks<ChartData>() {
+        @Override
+        public Loader<ChartData> onCreateLoader(int id, Bundle args) {
+            return new ChartDataLoader(getActivity(), mStatsSession, args);
+        }
+
+        @Override
+        public void onLoadFinished(Loader<ChartData> loader, ChartData data) {
+            mChartData = data;
+            mCycleAdapter.updateCycleList(mPolicy, mChartData);
+            bindData();
+        }
+
+        @Override
+        public void onLoaderReset(Loader<ChartData> loader) {
+        }
+    };
+
+    private class AppPrefLoader extends AsyncTask<String, Void, Preference> {
+        @Override
+        protected Preference doInBackground(String... params) {
+            PackageManager pm = getPackageManager();
+            String pkg = params[0];
+            try {
+                ApplicationInfo info = pm.getApplicationInfo(pkg, 0);
+                Preference preference = new Preference(getPrefContext());
+                preference.setIcon(info.loadIcon(pm));
+                preference.setTitle(info.loadLabel(pm));
+                preference.setSelectable(false);
+                return preference;
+            } catch (PackageManager.NameNotFoundException e) {
+            }
+            return null;
+        }
+
+        @Override
+        protected void onPostExecute(Preference pref) {
+            if (pref != null && mAppList != null) {
+                mAppList.addPreference(pref);
+            }
+        }
+    }
+}
diff --git a/src/com/android/settings/datausage/AppDataUsagePreference.java b/src/com/android/settings/datausage/AppDataUsagePreference.java
new file mode 100644
index 0000000..04e2b6d
--- /dev/null
+++ b/src/com/android/settings/datausage/AppDataUsagePreference.java
@@ -0,0 +1,123 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the
+ * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the specific language governing
+ * permissions and limitations under the License.
+ */
+
+package com.android.settings.datausage;
+
+import android.content.Context;
+import android.os.AsyncTask;
+import android.support.v7.preference.Preference;
+import android.support.v7.preference.PreferenceViewHolder;
+import android.text.format.Formatter;
+import android.view.View;
+import android.widget.ProgressBar;
+import com.android.settingslib.AppItem;
+import com.android.settingslib.net.UidDetail;
+import com.android.settingslib.net.UidDetailProvider;
+
+import static com.android.internal.util.Preconditions.checkNotNull;
+
+public class AppDataUsagePreference extends Preference {
+
+    private final AppItem mItem;
+    private final int mPercent;
+
+    public AppDataUsagePreference(Context context, AppItem item, int percent,
+            UidDetailProvider provider) {
+        super(context);
+        mItem = item;
+        mPercent = percent;
+        setLayoutResource(com.android.settings.R.layout.data_usage_item);
+        setWidgetLayoutResource(com.android.settings.R.layout.widget_progress_bar);
+        if (item.restricted && item.total <= 0) {
+            setSummary(com.android.settings.R.string.data_usage_app_restricted);
+        } else {
+            setSummary(Formatter.formatFileSize(context, item.total));
+        }
+
+        // kick off async load of app details
+        UidDetailTask.bindView(provider, item, this);
+    }
+
+    @Override
+    public void onBindViewHolder(PreferenceViewHolder holder) {
+        super.onBindViewHolder(holder);
+
+        final ProgressBar progress = (ProgressBar) holder.findViewById(
+                android.R.id.progress);
+
+        if (mItem.restricted && mItem.total <= 0) {
+            progress.setVisibility(View.GONE);
+        } else {
+            progress.setVisibility(View.VISIBLE);
+        }
+        progress.setProgress(mPercent);
+    }
+
+    public AppItem getItem() {
+        return mItem;
+    }
+
+    /**
+     * Background task that loads {@link UidDetail}, binding to
+     * {@link DataUsageAdapter} row item when finished.
+     */
+    private static class UidDetailTask extends AsyncTask<Void, Void, UidDetail> {
+        private final UidDetailProvider mProvider;
+        private final AppItem mItem;
+        private final AppDataUsagePreference mTarget;
+
+        private UidDetailTask(UidDetailProvider provider, AppItem item,
+                AppDataUsagePreference target) {
+            mProvider = checkNotNull(provider);
+            mItem = checkNotNull(item);
+            mTarget = checkNotNull(target);
+        }
+
+        public static void bindView(UidDetailProvider provider, AppItem item,
+                AppDataUsagePreference target) {
+            final UidDetail cachedDetail = provider.getUidDetail(item.key, false);
+            if (cachedDetail != null) {
+                bindView(cachedDetail, target);
+            } else {
+                new UidDetailTask(provider, item, target).executeOnExecutor(
+                        AsyncTask.THREAD_POOL_EXECUTOR);
+            }
+        }
+
+        private static void bindView(UidDetail detail, Preference target) {
+            if (detail != null) {
+                target.setIcon(detail.icon);
+                target.setTitle(detail.label);
+            } else {
+                target.setIcon(null);
+                target.setTitle(null);
+            }
+        }
+
+        @Override
+        protected void onPreExecute() {
+            bindView(null, mTarget);
+        }
+
+        @Override
+        protected UidDetail doInBackground(Void... params) {
+            return mProvider.getUidDetail(mItem.key, true);
+        }
+
+        @Override
+        protected void onPostExecute(UidDetail result) {
+            bindView(result, mTarget);
+        }
+    }
+}
diff --git a/src/com/android/settings/datausage/BillingCyclePreference.java b/src/com/android/settings/datausage/BillingCyclePreference.java
new file mode 100644
index 0000000..84eaabb
--- /dev/null
+++ b/src/com/android/settings/datausage/BillingCyclePreference.java
@@ -0,0 +1,90 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the
+ * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the specific language governing
+ * permissions and limitations under the License.
+ */
+
+package com.android.settings.datausage;
+
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.net.NetworkPolicy;
+import android.net.NetworkTemplate;
+import android.os.Bundle;
+import android.os.RemoteException;
+import android.support.v7.preference.Preference;
+import android.util.AttributeSet;
+import com.android.settings.R;
+import com.android.settings.Utils;
+
+public class BillingCyclePreference extends Preference implements TemplatePreference {
+
+    private NetworkTemplate mTemplate;
+    private NetworkServices mServices;
+    private NetworkPolicy mPolicy;
+    private int mSubId;
+
+    public BillingCyclePreference(Context context, AttributeSet attrs) {
+        super(context, attrs);
+    }
+
+    @Override
+    public void onAttached() {
+        super.onAttached();
+        getContext().registerReceiver(mReceiver, new IntentFilter(
+                CellDataPreference.ACTION_DATA_ENABLED_CHANGED));
+    }
+
+    @Override
+    public void onDetached() {
+        getContext().unregisterReceiver(mReceiver);
+        super.onDetached();
+    }
+
+    @Override
+    public void setTemplate(NetworkTemplate template, int subId,
+            NetworkServices services) {
+        mTemplate = template;
+        mSubId = subId;
+        mServices = services;
+        mPolicy = services.mPolicyEditor.getPolicy(mTemplate);
+        setSummary(getContext().getString(R.string.billing_cycle_fragment_summary,
+                mPolicy != null ? mPolicy.cycleDay : 1));
+        setIntent(getIntent());
+    }
+
+    private void updateEnabled() {
+        try {
+            setEnabled(mPolicy != null && mServices.mNetworkService.isBandwidthControlEnabled()
+                    && mServices.mTelephonyManager.getDataEnabled(mSubId)
+                    && mServices.mUserManager.isAdminUser());
+        } catch (RemoteException e) {
+            setEnabled(false);
+        }
+    }
+
+    @Override
+    public Intent getIntent() {
+        Bundle args = new Bundle();
+        args.putParcelable(DataUsageList.EXTRA_NETWORK_TEMPLATE, mTemplate);
+        return Utils.onBuildStartFragmentIntent(getContext(), BillingCycleSettings.class.getName(),
+                args, null, 0, getTitle(), false);
+    }
+
+    private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
+        @Override
+        public void onReceive(Context context, Intent intent) {
+            updateEnabled();
+        }
+    };
+}
diff --git a/src/com/android/settings/datausage/BillingCycleSettings.java b/src/com/android/settings/datausage/BillingCycleSettings.java
new file mode 100644
index 0000000..5807992
--- /dev/null
+++ b/src/com/android/settings/datausage/BillingCycleSettings.java
@@ -0,0 +1,357 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the
+ * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the specific language governing
+ * permissions and limitations under the License.
+ */
+
+package com.android.settings.datausage;
+
+import android.app.AlertDialog;
+import android.app.Dialog;
+import android.app.DialogFragment;
+import android.content.Context;
+import android.content.DialogInterface;
+import android.content.res.Resources;
+import android.net.NetworkPolicy;
+import android.net.NetworkTemplate;
+import android.os.Bundle;
+import android.support.v14.preference.SwitchPreference;
+import android.support.v7.preference.Preference;
+import android.text.format.Formatter;
+import android.text.format.Time;
+import android.util.Log;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.widget.NumberPicker;
+import com.android.settings.InstrumentedFragment;
+import com.android.settings.R;
+import com.android.settingslib.NetworkPolicyEditor;
+import com.android.settingslib.net.DataUsageController;
+
+import static android.net.NetworkPolicy.LIMIT_DISABLED;
+import static android.net.NetworkPolicy.WARNING_DISABLED;
+import static android.net.TrafficStats.GB_IN_BYTES;
+import static android.net.TrafficStats.MB_IN_BYTES;
+
+public class BillingCycleSettings extends DataUsageBase implements
+        Preference.OnPreferenceChangeListener {
+
+    private static final String TAG = "BillingCycleSettings";
+    private static final boolean LOGD = false;
+
+    private static final String TAG_CONFIRM_LIMIT = "confirmLimit";
+    private static final String TAG_CYCLE_EDITOR = "cycleEditor";
+    private static final String TAG_WARNING_EDITOR = "warningEditor";
+
+    private static final String KEY_BILLING_CYCLE = "billing_cycle";
+    private static final String KEY_DATA_WARNING = "data_warning";
+    private static final String KEY_SET_DATA_LIMIT = "set_data_limit";
+    private static final String KEY_DATA_LIMIT = "data_limit";
+
+    private NetworkTemplate mNetworkTemplate;
+    private Preference mBillingCycle;
+    private Preference mDataWarning;
+    private SwitchPreference mEnableDataLimit;
+    private Preference mDataLimit;
+    private DataUsageController mDataUsageController;
+
+    @Override
+    public void onCreate(Bundle icicle) {
+        super.onCreate(icicle);
+
+        mDataUsageController = new DataUsageController(getContext());
+
+        Bundle args = getArguments();
+        mNetworkTemplate = args.getParcelable(DataUsageList.EXTRA_NETWORK_TEMPLATE);
+
+        addPreferencesFromResource(R.xml.billing_cycle);
+        mBillingCycle = findPreference(KEY_BILLING_CYCLE);
+        mDataWarning = findPreference(KEY_DATA_WARNING);
+        mEnableDataLimit = (SwitchPreference) findPreference(KEY_SET_DATA_LIMIT);
+        mEnableDataLimit.setOnPreferenceChangeListener(this);
+        mDataLimit = findPreference(KEY_DATA_LIMIT);
+    }
+
+    @Override
+    public void onResume() {
+        super.onResume();
+        updatePrefs();
+    }
+
+    private void updatePrefs() {
+        NetworkPolicy policy = services.mPolicyEditor.getPolicy(mNetworkTemplate);
+        mBillingCycle.setSummary(getString(R.string.billing_cycle_summary, policy != null ?
+                policy.cycleDay : 1));
+        mDataWarning.setSummary(Formatter.formatFileSize(getContext(), policy != null
+                ? policy.warningBytes : DataUsageController.DEFAULT_WARNING_LEVEL));
+        if (policy != null && policy.limitBytes != LIMIT_DISABLED) {
+            mDataLimit.setSummary(Formatter.formatFileSize(getContext(), policy.limitBytes));
+            mDataLimit.setEnabled(true);
+            mEnableDataLimit.setChecked(true);
+        } else {
+            mDataLimit.setSummary(null);
+            mDataLimit.setEnabled(false);
+            mEnableDataLimit.setChecked(false);
+        }
+    }
+
+    @Override
+    public boolean onPreferenceTreeClick(Preference preference) {
+        if (preference == mBillingCycle) {
+            CycleEditorFragment.show(this);
+            return true;
+        } else if (preference == mDataWarning) {
+            BytesEditorFragment.show(this, false);
+            return true;
+        } else if (preference == mDataLimit) {
+            BytesEditorFragment.show(this, true);
+            return true;
+        }
+        return super.onPreferenceTreeClick(preference);
+    }
+
+    @Override
+    public boolean onPreferenceChange(Preference preference, Object newValue) {
+        if (mEnableDataLimit == preference) {
+            boolean enabled = (Boolean) newValue;
+            if (enabled) {
+                ConfirmLimitFragment.show(this);
+            } else {
+                setPolicyLimitBytes(LIMIT_DISABLED);
+            }
+            return true;
+        }
+        return false;
+    }
+
+    @Override
+    protected int getMetricsCategory() {
+        return InstrumentedFragment.BILLING_CYCLE;
+    }
+
+    private void setPolicyLimitBytes(long limitBytes) {
+        if (LOGD) Log.d(TAG, "setPolicyLimitBytes()");
+        services.mPolicyEditor.setPolicyLimitBytes(mNetworkTemplate, limitBytes);
+        updatePrefs();
+    }
+
+    /**
+     * Dialog to edit {@link NetworkPolicy#warningBytes}.
+     */
+    public static class BytesEditorFragment extends DialogFragment
+            implements DialogInterface.OnClickListener{
+        private static final String EXTRA_TEMPLATE = "template";
+        private static final String EXTRA_LIMIT = "limit";
+        private View mView;
+
+        public static void show(BillingCycleSettings parent, boolean isLimit) {
+            if (!parent.isAdded()) return;
+
+            final Bundle args = new Bundle();
+            args.putParcelable(EXTRA_TEMPLATE, parent.mNetworkTemplate);
+            args.putBoolean(EXTRA_LIMIT, isLimit);
+
+            final BytesEditorFragment dialog = new BytesEditorFragment();
+            dialog.setArguments(args);
+            dialog.setTargetFragment(parent, 0);
+            dialog.show(parent.getFragmentManager(), TAG_WARNING_EDITOR);
+        }
+
+        @Override
+        public Dialog onCreateDialog(Bundle savedInstanceState) {
+            final Context context = getActivity();
+
+
+            final LayoutInflater dialogInflater = LayoutInflater.from(context);
+            mView = dialogInflater.inflate(R.layout.data_usage_bytes_editor, null, false);
+            setupPicker((NumberPicker) mView.findViewById(R.id.bytes));
+            return new AlertDialog.Builder(context)
+                    .setTitle(R.string.data_usage_warning_editor_title)
+                    .setView(mView)
+                    .setPositiveButton(R.string.data_usage_cycle_editor_positive, this)
+                    .create();
+        }
+
+        private void setupPicker(NumberPicker bytesPicker) {
+            final BillingCycleSettings target = (BillingCycleSettings) getTargetFragment();
+            final NetworkPolicyEditor editor = target.services.mPolicyEditor;
+
+            final NetworkTemplate template = getArguments().getParcelable(EXTRA_TEMPLATE);
+            final boolean isLimit = getArguments().getBoolean(EXTRA_LIMIT);
+            final long warningBytes = editor.getPolicyWarningBytes(template);
+            final long limitBytes = editor.getPolicyLimitBytes(template);
+
+            if (isLimit) {
+                bytesPicker.setMaxValue(Integer.MAX_VALUE);
+                if (warningBytes != WARNING_DISABLED) {
+                    bytesPicker.setMinValue((int) (warningBytes / MB_IN_BYTES) + 1);
+                } else {
+                    bytesPicker.setMinValue(0);
+                }
+            } else {
+                bytesPicker.setMinValue(0);
+                if (limitBytes != LIMIT_DISABLED) {
+                    bytesPicker.setMaxValue((int) (limitBytes / MB_IN_BYTES) - 1);
+                } else {
+                    bytesPicker.setMaxValue(Integer.MAX_VALUE);
+                }
+            }
+            bytesPicker.setValue((int) ((isLimit ? limitBytes : warningBytes) / MB_IN_BYTES));
+            bytesPicker.setWrapSelectorWheel(false);
+        }
+
+        @Override
+        public void onClick(DialogInterface dialog, int which) {
+            if (which != DialogInterface.BUTTON_POSITIVE) {
+                return;
+            }
+            final BillingCycleSettings target = (BillingCycleSettings) getTargetFragment();
+            final NetworkPolicyEditor editor = target.services.mPolicyEditor;
+
+            final NetworkTemplate template = getArguments().getParcelable(EXTRA_TEMPLATE);
+            final boolean isLimit = getArguments().getBoolean(EXTRA_LIMIT);
+            NumberPicker bytesPicker = (NumberPicker) mView.findViewById(R.id.bytes);
+            // clear focus to finish pending text edits
+            bytesPicker.clearFocus();
+
+            final long bytes = bytesPicker.getValue() * MB_IN_BYTES;
+            if (isLimit) {
+                editor.setPolicyLimitBytes(template, bytes);
+            } else {
+                editor.setPolicyWarningBytes(template, bytes);
+            }
+            target.updatePrefs();
+        }
+    }
+
+    /**
+     * Dialog to edit {@link NetworkPolicy#cycleDay}.
+     */
+    public static class CycleEditorFragment extends DialogFragment implements
+            DialogInterface.OnClickListener{
+        private static final String EXTRA_TEMPLATE = "template";
+        private NumberPicker mCycleDayPicker;
+
+        public static void show(BillingCycleSettings parent) {
+            if (!parent.isAdded()) return;
+
+            final Bundle args = new Bundle();
+            args.putParcelable(EXTRA_TEMPLATE, parent.mNetworkTemplate);
+
+            final CycleEditorFragment dialog = new CycleEditorFragment();
+            dialog.setArguments(args);
+            dialog.setTargetFragment(parent, 0);
+            dialog.show(parent.getFragmentManager(), TAG_CYCLE_EDITOR);
+        }
+
+        @Override
+        public Dialog onCreateDialog(Bundle savedInstanceState) {
+            final Context context = getActivity();
+            final BillingCycleSettings target = (BillingCycleSettings) getTargetFragment();
+            final NetworkPolicyEditor editor = target.services.mPolicyEditor;
+
+            final AlertDialog.Builder builder = new AlertDialog.Builder(context);
+            final LayoutInflater dialogInflater = LayoutInflater.from(builder.getContext());
+
+            final View view = dialogInflater.inflate(R.layout.data_usage_cycle_editor, null, false);
+            mCycleDayPicker = (NumberPicker) view.findViewById(R.id.cycle_day);
+
+            final NetworkTemplate template = getArguments().getParcelable(EXTRA_TEMPLATE);
+            final int cycleDay = editor.getPolicyCycleDay(template);
+
+            mCycleDayPicker.setMinValue(1);
+            mCycleDayPicker.setMaxValue(31);
+            mCycleDayPicker.setValue(cycleDay);
+            mCycleDayPicker.setWrapSelectorWheel(true);
+
+            return builder.setTitle(R.string.data_usage_cycle_editor_title)
+                    .setView(view)
+                    .setPositiveButton(R.string.data_usage_cycle_editor_positive, this)
+                    .create();
+        }
+
+        @Override
+        public void onClick(DialogInterface dialog, int which) {
+            final NetworkTemplate template = getArguments().getParcelable(EXTRA_TEMPLATE);
+            final BillingCycleSettings target = (BillingCycleSettings) getTargetFragment();
+            final NetworkPolicyEditor editor = target.services.mPolicyEditor;
+
+            // clear focus to finish pending text edits
+            mCycleDayPicker.clearFocus();
+
+            final int cycleDay = mCycleDayPicker.getValue();
+            final String cycleTimezone = new Time().timezone;
+            editor.setPolicyCycleDay(template, cycleDay, cycleTimezone);
+            target.updatePrefs();
+        }
+    }
+
+    /**
+     * Dialog to request user confirmation before setting
+     * {@link NetworkPolicy#limitBytes}.
+     */
+    public static class ConfirmLimitFragment extends DialogFragment implements
+            DialogInterface.OnClickListener{
+        private static final String EXTRA_MESSAGE = "message";
+        private static final String EXTRA_LIMIT_BYTES = "limitBytes";
+        public static final float FLOAT = 1.2f;
+
+        public static void show(BillingCycleSettings parent) {
+            if (!parent.isAdded()) return;
+
+            final NetworkPolicy policy = parent.services.mPolicyEditor
+                    .getPolicy(parent.mNetworkTemplate);
+            if (policy == null) return;
+
+            final Resources res = parent.getResources();
+            final CharSequence message;
+            final long minLimitBytes = (long) (policy.warningBytes * FLOAT);
+            final long limitBytes;
+
+            // TODO: customize default limits based on network template
+            message = res.getString(R.string.data_usage_limit_dialog_mobile);
+            limitBytes = Math.max(5 * GB_IN_BYTES, minLimitBytes);
+
+            final Bundle args = new Bundle();
+            args.putCharSequence(EXTRA_MESSAGE, message);
+            args.putLong(EXTRA_LIMIT_BYTES, limitBytes);
+
+            final ConfirmLimitFragment dialog = new ConfirmLimitFragment();
+            dialog.setArguments(args);
+            dialog.setTargetFragment(parent, 0);
+            dialog.show(parent.getFragmentManager(), TAG_CONFIRM_LIMIT);
+        }
+
+        @Override
+        public Dialog onCreateDialog(Bundle savedInstanceState) {
+            final Context context = getActivity();
+
+            final CharSequence message = getArguments().getCharSequence(EXTRA_MESSAGE);
+
+            return new AlertDialog.Builder(context)
+                    .setTitle(R.string.data_usage_limit_dialog_title)
+                    .setMessage(message)
+                    .setPositiveButton(android.R.string.ok, this)
+                    .setNegativeButton(android.R.string.cancel, null)
+                    .create();
+        }
+
+        @Override
+        public void onClick(DialogInterface dialog, int which) {
+            if (which != DialogInterface.BUTTON_POSITIVE) return;
+            final long limitBytes = getArguments().getLong(EXTRA_LIMIT_BYTES);
+            final BillingCycleSettings target = (BillingCycleSettings) getTargetFragment();
+            if (target != null) {
+                target.setPolicyLimitBytes(limitBytes);
+            }
+        }
+    }
+}
diff --git a/src/com/android/settings/datausage/CellDataPreference.java b/src/com/android/settings/datausage/CellDataPreference.java
new file mode 100644
index 0000000..0a91be2
--- /dev/null
+++ b/src/com/android/settings/datausage/CellDataPreference.java
@@ -0,0 +1,276 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the
+ * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the specific language governing
+ * permissions and limitations under the License.
+ */
+
+package com.android.settings.datausage;
+
+import android.app.AlertDialog;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.DialogInterface;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.net.NetworkTemplate;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.support.v7.preference.PreferenceViewHolder;
+import android.telephony.SubscriptionInfo;
+import android.telephony.SubscriptionManager;
+import android.telephony.TelephonyManager;
+import android.util.AttributeSet;
+import android.util.Log;
+import android.view.View;
+import android.widget.Checkable;
+import com.android.internal.logging.MetricsLogger;
+import com.android.settings.CustomDialogPreference;
+import com.android.settings.R;
+import com.android.settings.Utils;
+
+import java.util.List;
+
+public class CellDataPreference extends CustomDialogPreference implements TemplatePreference {
+
+    // TODO: Get Telephony to broadcast when this changes, and remove this.
+    static final String ACTION_DATA_ENABLED_CHANGED =
+            "com.android.settings.action.DATA_ENABLED_CHANGED";
+
+    private static final String TAG = "CellDataPreference";
+
+    public int mSubId;
+    public boolean mChecked;
+    public boolean mMultiSimDialog;
+    private TelephonyManager mTelephonyManager;
+    private SubscriptionManager mSubscriptionManager;
+
+    public CellDataPreference(Context context, AttributeSet attrs) {
+        super(context, attrs, android.R.attr.switchPreferenceStyle);
+    }
+
+    @Override
+    protected void onRestoreInstanceState(Parcelable s) {
+        CellDataState state = (CellDataState) s;
+        super.onRestoreInstanceState(state.getSuperState());
+        mTelephonyManager = TelephonyManager.from(getContext());
+        mChecked = state.mChecked;
+        mMultiSimDialog = state.mMultiSimDialog;
+        mSubId = state.mSubId;
+        setKey(getKey() + mSubId);
+        notifyChanged();
+    }
+
+    @Override
+    protected Parcelable onSaveInstanceState() {
+        CellDataState state = new CellDataState(super.onSaveInstanceState());
+        state.mChecked = mChecked;
+        state.mMultiSimDialog = mMultiSimDialog;
+        state.mSubId = mSubId;
+        return state;
+    }
+
+    @Override
+    public void onAttached() {
+        super.onAttached();
+        getContext().registerReceiver(mReceiver, new IntentFilter(ACTION_DATA_ENABLED_CHANGED));
+    }
+
+    @Override
+    public void onDetached() {
+        getContext().unregisterReceiver(mReceiver);
+        super.onDetached();
+    }
+
+    @Override
+    public void setTemplate(NetworkTemplate template, int subId, NetworkServices services) {
+        if (subId == SubscriptionManager.INVALID_SUBSCRIPTION_ID) {
+            throw new IllegalArgumentException("CellDataPreference needs a SubscriptionInfo");
+        }
+        mTelephonyManager = TelephonyManager.from(getContext());
+        if (mSubId == SubscriptionManager.INVALID_SUBSCRIPTION_ID) {
+            mSubId = subId;
+            setKey(getKey() + subId);
+        }
+        updateChecked();
+    }
+
+    private void updateChecked() {
+        setChecked(mTelephonyManager.getDataEnabled(mSubId));
+    }
+
+    @Override
+    protected void performClick(View view) {
+        super.performClick(view);
+        MetricsLogger.action(getContext(), MetricsLogger.ACTION_CELL_DATA_TOGGLE, !mChecked);
+        if (mChecked) {
+            // disabling data; show confirmation dialog which eventually
+            // calls setMobileDataEnabled() once user confirms.
+            mMultiSimDialog = false;
+            super.performClick(view);
+        } else {
+            // If we are showing the Sim Card tile then we are a Multi-Sim device.
+            if (Utils.showSimCardTile(getContext())) {
+                mMultiSimDialog = true;
+                super.performClick(view);
+            } else {
+                setMobileDataEnabled(true);
+            }
+        }
+    }
+
+    private void setMobileDataEnabled(boolean enabled) {
+        if (DataUsageSummary.LOGD) Log.d(TAG, "setMobileDataEnabled()");
+        mTelephonyManager.setDataEnabled(mSubId, enabled);
+        setChecked(enabled);
+        getContext().sendBroadcast(new Intent(ACTION_DATA_ENABLED_CHANGED));
+    }
+
+    private void setChecked(boolean checked) {
+        if (mChecked == checked) return;
+        mChecked = checked;
+        notifyChanged();
+    }
+
+    @Override
+    public void onBindViewHolder(PreferenceViewHolder holder) {
+        super.onBindViewHolder(holder);
+        View switchView = holder.findViewById(android.R.id.switch_widget);
+        switchView.setClickable(false);
+        ((Checkable) switchView).setChecked(mChecked);
+    }
+
+    @Override
+    protected void onPrepareDialogBuilder(AlertDialog.Builder builder,
+            DialogInterface.OnClickListener listener) {
+        if (mMultiSimDialog) {
+            showMultiSimDialog(builder, listener);
+        } else {
+            showDisableDialog(builder, listener);
+        }
+    }
+
+    private void showDisableDialog(AlertDialog.Builder builder,
+            DialogInterface.OnClickListener listener) {
+        builder.setTitle(null)
+                .setMessage(R.string.data_usage_disable_mobile)
+                .setPositiveButton(android.R.string.ok, listener)
+                .setNegativeButton(android.R.string.cancel, null);
+    }
+
+    private void showMultiSimDialog(AlertDialog.Builder builder,
+            DialogInterface.OnClickListener listener) {
+        mSubscriptionManager = SubscriptionManager.from(getContext());
+        final SubscriptionInfo currentSir = mSubscriptionManager.getActiveSubscriptionInfo(mSubId);
+
+        final SubscriptionInfo nextSir = mSubscriptionManager.getDefaultDataSubscriptionInfo();
+
+        // If the device is single SIM or is enabling data on the active data SIM then forgo
+        // the pop-up.
+        if (!Utils.showSimCardTile(getContext()) ||
+                (nextSir != null && currentSir != null &&
+                currentSir.getSubscriptionId() == nextSir.getSubscriptionId())) {
+            setMobileDataEnabled(true);
+            if (nextSir != null && currentSir != null &&
+                currentSir.getSubscriptionId() == nextSir.getSubscriptionId()) {
+                disableDataForOtherSubscriptions(currentSir);
+            }
+            return;
+        }
+
+        final String previousName = (nextSir == null)
+            ? getContext().getResources().getString(R.string.sim_selection_required_pref)
+            : nextSir.getDisplayName().toString();
+
+        builder.setTitle(R.string.sim_change_data_title);
+        builder.setMessage(getContext().getString(R.string.sim_change_data_message,
+                    currentSir.getDisplayName(), previousName));
+
+        builder.setPositiveButton(R.string.okay, new DialogInterface.OnClickListener() {
+            @Override
+            public void onClick(DialogInterface dialog, int id) {
+                mSubscriptionManager.setDefaultDataSubId(currentSir.getSubscriptionId());
+                setMobileDataEnabled(true);
+                disableDataForOtherSubscriptions(currentSir);
+            }
+        });
+        builder.setNegativeButton(R.string.cancel, null);
+
+        builder.create().show();
+    }
+
+    private void disableDataForOtherSubscriptions(SubscriptionInfo currentSir) {
+        List<SubscriptionInfo> subInfoList = mSubscriptionManager.getActiveSubscriptionInfoList();
+        if (subInfoList != null) {
+            for (SubscriptionInfo subInfo : subInfoList) {
+                if (subInfo.getSubscriptionId() != currentSir.getSubscriptionId()) {
+                    mTelephonyManager.setDataEnabled(subInfo.getSubscriptionId(), false);
+                }
+            }
+        }
+        getContext().sendBroadcast(new Intent(ACTION_DATA_ENABLED_CHANGED));
+    }
+
+    @Override
+    protected void onClick(DialogInterface dialog, int which) {
+        if (which != DialogInterface.BUTTON_POSITIVE) {
+            return;
+        }
+        if (mMultiSimDialog) {
+        } else {
+            // TODO: extend to modify policy enabled flag.
+            setMobileDataEnabled(false);
+        }
+    }
+
+    private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
+        @Override
+        public void onReceive(Context context, Intent intent) {
+            updateChecked();
+        }
+    };
+
+    public static class CellDataState extends BaseSavedState {
+        public int mSubId;
+        public boolean mChecked;
+        public boolean mMultiSimDialog;
+
+        public CellDataState(Parcelable base) {
+            super(base);
+        }
+
+        public CellDataState(Parcel source) {
+            super(source);
+            mChecked = source.readByte() != 0;
+            mMultiSimDialog = source.readByte() != 0;
+            mSubId = source.readInt();
+        }
+
+        @Override
+        public void writeToParcel(Parcel dest, int flags) {
+            super.writeToParcel(dest, flags);
+            dest.writeByte((byte) (mChecked ? 1 : 0));
+            dest.writeByte((byte) (mMultiSimDialog ? 1 : 0));
+            dest.writeInt(mSubId);
+        }
+
+        public static final Creator<CellDataState> CREATOR = new Creator<CellDataState>() {
+            @Override
+            public CellDataState createFromParcel(Parcel source) {
+                return new CellDataState(source);
+            }
+
+            @Override
+            public CellDataState[] newArray(int size) {
+                return new CellDataState[size];
+            }
+        };
+    }
+}
diff --git a/src/com/android/settings/datausage/ChartDataUsagePreference.java b/src/com/android/settings/datausage/ChartDataUsagePreference.java
new file mode 100644
index 0000000..f328cf6
--- /dev/null
+++ b/src/com/android/settings/datausage/ChartDataUsagePreference.java
@@ -0,0 +1,82 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the
+ * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the specific language governing
+ * permissions and limitations under the License.
+ */
+
+package com.android.settings.datausage;
+
+import android.content.Context;
+import android.graphics.Color;
+import android.net.NetworkPolicy;
+import android.net.NetworkStatsHistory;
+import android.support.v7.preference.Preference;
+import android.support.v7.preference.PreferenceViewHolder;
+import android.util.AttributeSet;
+import com.android.settings.R;
+import com.android.settings.widget.ChartDataUsageView;
+import com.android.settings.widget.ChartNetworkSeriesView;
+
+public class ChartDataUsagePreference extends Preference {
+
+    private NetworkPolicy mPolicy;
+    private long mStart;
+    private long mEnd;
+    private NetworkStatsHistory mNetwork;
+    private int mSecondaryColor;
+    private int mSeriesColor;
+
+    public ChartDataUsagePreference(Context context, AttributeSet attrs) {
+        super(context, attrs);
+        setLayoutResource(R.layout.data_usage_chart);
+    }
+
+    @Override
+    public void onBindViewHolder(PreferenceViewHolder holder) {
+        super.onBindViewHolder(holder);
+        ChartDataUsageView chart = (ChartDataUsageView) holder.itemView;
+        chart.setVisibleRange(mStart, mEnd);
+        chart.bindNetworkPolicy(mPolicy);
+        chart.bindNetworkStats(mNetwork);
+        ChartNetworkSeriesView series = (ChartNetworkSeriesView) holder.findViewById(R.id.series);
+        series.setChartColor(Color.BLACK, mSeriesColor, mSecondaryColor);
+    }
+
+    public void bindNetworkPolicy(NetworkPolicy policy) {
+        mPolicy = policy;
+        notifyChanged();
+    }
+
+    public void setVisibleRange(long start, long end) {
+        mStart = start;
+        mEnd = end;
+        notifyChanged();
+    }
+
+    public long getInspectStart() {
+        return mStart;
+    }
+
+    public long getInspectEnd() {
+        return mEnd;
+    }
+
+    public void bindNetworkStats(NetworkStatsHistory network) {
+        mNetwork = network;
+        notifyChanged();
+    }
+
+    public void setColors(int seriesColor, int secondaryColor) {
+        mSeriesColor = seriesColor;
+        mSecondaryColor = secondaryColor;
+        notifyChanged();
+    }
+}
diff --git a/src/com/android/settings/datausage/CycleAdapter.java b/src/com/android/settings/datausage/CycleAdapter.java
new file mode 100644
index 0000000..682cc8a
--- /dev/null
+++ b/src/com/android/settings/datausage/CycleAdapter.java
@@ -0,0 +1,188 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the
+ * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the specific language governing
+ * permissions and limitations under the License.
+ */
+package com.android.settings.datausage;
+
+import com.android.settings.Utils;
+import com.android.settingslib.net.ChartData;
+
+import android.content.Context;
+import android.net.NetworkPolicy;
+import android.net.NetworkStatsHistory;
+import android.text.format.DateUtils;
+import android.widget.AdapterView;
+import android.widget.ArrayAdapter;
+import android.widget.Spinner;
+
+import libcore.util.Objects;
+
+import static android.net.NetworkPolicyManager.computeLastCycleBoundary;
+import static android.net.NetworkPolicyManager.computeNextCycleBoundary;
+
+public class CycleAdapter extends ArrayAdapter<CycleAdapter.CycleItem> {
+
+    private final Spinner mSpinner;
+    private final AdapterView.OnItemSelectedListener mListener;
+
+    public CycleAdapter(Context context, Spinner spinner,
+            AdapterView.OnItemSelectedListener listener) {
+        super(context, com.android.settings.R.layout.filter_spinner_item);
+        setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
+        mSpinner = spinner;
+        mListener = listener;
+        mSpinner.setAdapter(this);
+        mSpinner.setOnItemSelectedListener(mListener);
+    }
+
+    /**
+     * Find position of {@link CycleItem} in this adapter which is nearest
+     * the given {@link CycleItem}.
+     */
+    public int findNearestPosition(CycleItem target) {
+        if (target != null) {
+            final int count = getCount();
+            for (int i = count - 1; i >= 0; i--) {
+                final CycleItem item = getItem(i);
+                if (item.compareTo(target) >= 0) {
+                    return i;
+                }
+            }
+        }
+        return 0;
+    }
+
+    /**
+     * Rebuild list based on {@link NetworkPolicy#cycleDay}
+     * and available {@link NetworkStatsHistory} data. Always selects the newest
+     * item, updating the inspection range on chartData.
+     */
+     public boolean updateCycleList(NetworkPolicy policy, ChartData chartData) {
+        // stash away currently selected cycle to try restoring below
+        final CycleAdapter.CycleItem previousItem = (CycleAdapter.CycleItem)
+                mSpinner.getSelectedItem();
+        clear();
+
+        final Context context = mSpinner.getContext();
+        NetworkStatsHistory.Entry entry = null;
+
+        long historyStart = Long.MAX_VALUE;
+        long historyEnd = Long.MIN_VALUE;
+        if (chartData != null) {
+            historyStart = chartData.network.getStart();
+            historyEnd = chartData.network.getEnd();
+        }
+
+        final long now = System.currentTimeMillis();
+        if (historyStart == Long.MAX_VALUE) historyStart = now;
+        if (historyEnd == Long.MIN_VALUE) historyEnd = now + 1;
+
+        boolean hasCycles = false;
+        if (policy != null) {
+            // find the next cycle boundary
+            long cycleEnd = computeNextCycleBoundary(historyEnd, policy);
+
+            // walk backwards, generating all valid cycle ranges
+            while (cycleEnd > historyStart) {
+                final long cycleStart = computeLastCycleBoundary(cycleEnd, policy);
+
+                final boolean includeCycle;
+                if (chartData != null) {
+                    entry = chartData.network.getValues(cycleStart, cycleEnd, entry);
+                    includeCycle = (entry.rxBytes + entry.txBytes) > 0;
+                } else {
+                    includeCycle = true;
+                }
+
+                if (includeCycle) {
+                    add(new CycleAdapter.CycleItem(context, cycleStart, cycleEnd));
+                    hasCycles = true;
+                }
+                cycleEnd = cycleStart;
+            }
+        }
+
+        if (!hasCycles) {
+            // no policy defined cycles; show entry for each four-week period
+            long cycleEnd = historyEnd;
+            while (cycleEnd > historyStart) {
+                final long cycleStart = cycleEnd - (DateUtils.WEEK_IN_MILLIS * 4);
+
+                final boolean includeCycle;
+                if (chartData != null) {
+                    entry = chartData.network.getValues(cycleStart, cycleEnd, entry);
+                    includeCycle = (entry.rxBytes + entry.txBytes) > 0;
+                } else {
+                    includeCycle = true;
+                }
+
+                if (includeCycle) {
+                    add(new CycleAdapter.CycleItem(context, cycleStart, cycleEnd));
+                }
+                cycleEnd = cycleStart;
+            }
+        }
+
+        // force pick the current cycle (first item)
+        if (getCount() > 0) {
+            final int position = findNearestPosition(previousItem);
+            mSpinner.setSelection(position);
+
+            // only force-update cycle when changed; skipping preserves any
+            // user-defined inspection region.
+            final CycleAdapter.CycleItem selectedItem = getItem(position);
+            if (!Objects.equal(selectedItem, previousItem)) {
+                mListener.onItemSelected(mSpinner, null, position, 0);
+                return false;
+            }
+        }
+        return true;
+    }
+
+    /**
+     * List item that reflects a specific data usage cycle.
+     */
+    public static class CycleItem implements Comparable<CycleItem> {
+        public CharSequence label;
+        public long start;
+        public long end;
+
+        public CycleItem(CharSequence label) {
+            this.label = label;
+        }
+
+        public CycleItem(Context context, long start, long end) {
+            this.label = Utils.formatDateRange(context, start, end);
+            this.start = start;
+            this.end = end;
+        }
+
+        @Override
+        public String toString() {
+            return label.toString();
+        }
+
+        @Override
+        public boolean equals(Object o) {
+            if (o instanceof CycleItem) {
+                final CycleItem another = (CycleItem) o;
+                return start == another.start && end == another.end;
+            }
+            return false;
+        }
+
+        @Override
+        public int compareTo(CycleItem another) {
+            return Long.compare(start, another.start);
+        }
+    }
+}
diff --git a/src/com/android/settings/datausage/DataUsageBase.java b/src/com/android/settings/datausage/DataUsageBase.java
new file mode 100644
index 0000000..ef565a1
--- /dev/null
+++ b/src/com/android/settings/datausage/DataUsageBase.java
@@ -0,0 +1,92 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the
+ * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the specific language governing
+ * permissions and limitations under the License.
+ */
+
+package com.android.settings.datausage;
+
+import com.android.settings.SettingsPreferenceFragment;
+import com.android.settingslib.NetworkPolicyEditor;
+
+import android.content.Context;
+import android.net.INetworkStatsService;
+import android.net.NetworkPolicy;
+import android.net.NetworkPolicyManager;
+import android.os.Bundle;
+import android.os.INetworkManagementService;
+import android.os.RemoteException;
+import android.os.ServiceManager;
+import android.os.UserManager;
+import android.telephony.SubscriptionManager;
+import android.telephony.TelephonyManager;
+import android.util.Log;
+
+public abstract class DataUsageBase extends SettingsPreferenceFragment {
+
+    private static final String TAG = "DataUsageBase";
+
+    protected final TemplatePreference.NetworkServices services =
+            new TemplatePreference.NetworkServices();
+
+    @Override
+    public void onCreate(Bundle icicle) {
+        super.onCreate(icicle);
+        final Context context = getActivity();
+
+        services.mNetworkService = INetworkManagementService.Stub.asInterface(
+                ServiceManager.getService(Context.NETWORKMANAGEMENT_SERVICE));
+        services.mStatsService = INetworkStatsService.Stub.asInterface(
+                ServiceManager.getService(Context.NETWORK_STATS_SERVICE));
+        services.mPolicyManager = NetworkPolicyManager.from(context);
+
+        services.mPolicyEditor = new NetworkPolicyEditor(services.mPolicyManager);
+
+        services.mTelephonyManager = TelephonyManager.from(context);
+        services.mSubscriptionManager = SubscriptionManager.from(context);
+        services.mUserManager = UserManager.get(context);
+    }
+
+    @Override
+    public void onResume() {
+        super.onResume();
+        services.mPolicyEditor.read();
+    }
+
+    protected boolean isAdmin() {
+        return services.mUserManager.isAdminUser();
+    }
+
+    protected boolean isMobileDataAvailable(int subId) {
+        return services.mSubscriptionManager.getActiveSubscriptionInfo(subId) != null;
+    }
+
+    protected boolean isNetworkPolicyModifiable(NetworkPolicy policy, int subId) {
+        return policy != null && isBandwidthControlEnabled() && services.mUserManager.isAdminUser()
+                && isDataEnabled(subId);
+    }
+
+    private boolean isDataEnabled(int subId) {
+        if (subId == SubscriptionManager.INVALID_SUBSCRIPTION_ID) {
+            return true;
+        }
+        return services.mTelephonyManager.getDataEnabled(subId);
+    }
+
+    protected boolean isBandwidthControlEnabled() {
+        try {
+            return services.mNetworkService.isBandwidthControlEnabled();
+        } catch (RemoteException e) {
+            Log.w(TAG, "problem talking with INetworkManagementService: " + e);
+            return false;
+        }
+    }
+}
diff --git a/src/com/android/settings/datausage/DataUsageList.java b/src/com/android/settings/datausage/DataUsageList.java
new file mode 100644
index 0000000..4aa52ba
--- /dev/null
+++ b/src/com/android/settings/datausage/DataUsageList.java
@@ -0,0 +1,550 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the
+ * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the specific language governing
+ * permissions and limitations under the License.
+ */
+
+package com.android.settings.datausage;
+
+import android.app.ActivityManager;
+import android.app.LoaderManager.LoaderCallbacks;
+import android.content.Context;
+import android.content.Loader;
+import android.content.pm.UserInfo;
+import android.graphics.Color;
+import android.net.ConnectivityManager;
+import android.net.INetworkStatsSession;
+import android.net.NetworkPolicy;
+import android.net.NetworkStats;
+import android.net.NetworkStatsHistory;
+import android.net.NetworkTemplate;
+import android.net.TrafficStats;
+import android.os.AsyncTask;
+import android.os.Bundle;
+import android.os.RemoteException;
+import android.os.SystemProperties;
+import android.os.UserHandle;
+import android.os.UserManager;
+import android.support.v7.preference.Preference;
+import android.support.v7.preference.PreferenceGroup;
+import android.telephony.SubscriptionInfo;
+import android.telephony.SubscriptionManager;
+import android.telephony.TelephonyManager;
+import android.text.format.DateUtils;
+import android.text.format.Formatter;
+import android.util.Log;
+import android.util.SparseArray;
+import android.view.View;
+import android.widget.AdapterView;
+import android.widget.AdapterView.OnItemSelectedListener;
+import android.widget.Spinner;
+import com.android.settings.InstrumentedFragment;
+import com.android.settings.R;
+import com.android.settingslib.AppItem;
+import com.android.settingslib.net.ChartData;
+import com.android.settingslib.net.ChartDataLoader;
+import com.android.settingslib.net.SummaryForAllUidLoader;
+import com.android.settingslib.net.UidDetailProvider;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import static android.net.ConnectivityManager.TYPE_MOBILE;
+import static android.net.NetworkPolicyManager.POLICY_REJECT_METERED_BACKGROUND;
+import static android.net.TrafficStats.UID_REMOVED;
+import static android.net.TrafficStats.UID_TETHERING;
+import static android.telephony.TelephonyManager.SIM_STATE_READY;
+import static com.android.settings.datausage.DataUsageSummary.TEST_RADIOS;
+import static com.android.settings.datausage.DataUsageSummary.TEST_RADIOS_PROP;
+
+/**
+ * Panel showing data usage history across various networks, including options
+ * to inspect based on usage cycle and control through {@link NetworkPolicy}.
+ */
+public class DataUsageList extends DataUsageBase {
+    private static final String TAG = "DataUsage";
+    private static final boolean LOGD = false;
+
+    private static final String KEY_USAGE_AMOUNT = "usage_amount";
+    private static final String KEY_CHART_DATA = "chart_data";
+    private static final String KEY_APPS_GROUP = "apps_group";
+
+    private static final int LOADER_CHART_DATA = 2;
+    private static final int LOADER_SUMMARY = 3;
+    public static final String EXTRA_SUB_ID = "sub_id";
+    public static final String EXTRA_NETWORK_TEMPLATE = "network_template";
+
+    private INetworkStatsSession mStatsSession;
+
+    private ChartDataUsagePreference mChart;
+
+    private NetworkTemplate mTemplate;
+    private int mSubId;
+    private ChartData mChartData;
+
+    /** Flag used to ignore listeners during binding. */
+    private boolean mBinding;
+
+    private UidDetailProvider mUidDetailProvider;
+
+    /**
+     * Local cache of data enabled for subId, used to work around delays.
+     */
+    private final Map<String, Boolean> mMobileDataEnabled = new HashMap<String, Boolean>();
+    private CycleAdapter mCycleAdapter;
+    private Spinner mCycleSpinner;
+    private Preference mUsageAmount;
+    private PreferenceGroup mApps;
+    private View mHeader;
+
+    @Override
+    protected int getMetricsCategory() {
+        return InstrumentedFragment.DATA_USAGE_LIST;
+    }
+
+    @Override
+    public void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        final Context context = getActivity();
+
+        if (!isBandwidthControlEnabled()) {
+            Log.w(TAG, "No bandwidth control; leaving");
+            getActivity().finish();
+        }
+
+        try {
+            mStatsSession = services.mStatsService.openSession();
+        } catch (RemoteException e) {
+            throw new RuntimeException(e);
+        }
+
+        mUidDetailProvider = new UidDetailProvider(context);
+
+        addPreferencesFromResource(R.xml.data_usage_list);
+        mUsageAmount = findPreference(KEY_USAGE_AMOUNT);
+        mChart = (ChartDataUsagePreference) findPreference(KEY_CHART_DATA);
+        mApps = (PreferenceGroup) findPreference(KEY_APPS_GROUP);
+
+        final Bundle args = getArguments();
+        mSubId = args.getInt(EXTRA_SUB_ID, SubscriptionManager.INVALID_SUBSCRIPTION_ID);
+        mTemplate = args.getParcelable(EXTRA_NETWORK_TEMPLATE);
+    }
+
+    @Override
+    public void onViewCreated(View v, Bundle savedInstanceState) {
+        super.onViewCreated(v, savedInstanceState);
+
+        mHeader = setPinnedHeaderView(R.layout.apps_filter_spinner);
+        mCycleSpinner = (Spinner) mHeader.findViewById(R.id.filter_spinner);
+        mCycleAdapter = new CycleAdapter(getContext(), mCycleSpinner, mCycleListener);
+        setLoading(true, false);
+    }
+
+    @Override
+    public void onResume() {
+        super.onResume();
+
+        updateBody();
+
+        // kick off background task to update stats
+        new AsyncTask<Void, Void, Void>() {
+            @Override
+            protected Void doInBackground(Void... params) {
+                try {
+                    // wait a few seconds before kicking off
+                    Thread.sleep(2 * DateUtils.SECOND_IN_MILLIS);
+                    services.mStatsService.forceUpdate();
+                } catch (InterruptedException e) {
+                } catch (RemoteException e) {
+                }
+                return null;
+            }
+
+            @Override
+            protected void onPostExecute(Void result) {
+                if (isAdded()) {
+                    updateBody();
+                }
+            }
+        }.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
+    }
+
+    @Override
+    public void onDestroy() {
+        mUidDetailProvider.clearCache();
+        mUidDetailProvider = null;
+
+        TrafficStats.closeQuietly(mStatsSession);
+
+        super.onDestroy();
+    }
+
+    /**
+     * Update body content based on current tab. Loads
+     * {@link NetworkStatsHistory} and {@link NetworkPolicy} from system, and
+     * binds them to visible controls.
+     */
+    private void updateBody() {
+        mBinding = true;
+        if (!isAdded()) return;
+
+        final Context context = getActivity();
+
+        // kick off loader for network history
+        // TODO: consider chaining two loaders together instead of reloading
+        // network history when showing app detail.
+        getLoaderManager().restartLoader(LOADER_CHART_DATA,
+                ChartDataLoader.buildArgs(mTemplate, null), mChartDataCallbacks);
+
+        // detail mode can change visible menus, invalidate
+        getActivity().invalidateOptionsMenu();
+
+        mBinding = false;
+
+        int seriesColor = context.getColor(R.color.sim_noitification);
+        if (mSubId != SubscriptionManager.INVALID_SUBSCRIPTION_ID){
+            final SubscriptionInfo sir = services.mSubscriptionManager
+                    .getActiveSubscriptionInfo(mSubId);
+
+            if (sir != null) {
+                seriesColor = sir.getIconTint();
+            }
+        }
+
+        final int secondaryColor = Color.argb(127, Color.red(seriesColor), Color.green(seriesColor),
+                Color.blue(seriesColor));
+        mChart.setColors(seriesColor, secondaryColor);
+    }
+
+    /**
+     * Update chart sweeps and cycle list to reflect {@link NetworkPolicy} for
+     * current {@link #mTemplate}.
+     */
+    private void updatePolicy(boolean refreshCycle) {
+        final NetworkPolicy policy = services.mPolicyEditor.getPolicy(mTemplate);
+        //SUB SELECT
+        if (isNetworkPolicyModifiable(policy, mSubId) && isMobileDataAvailable(mSubId)) {
+            mChart.bindNetworkPolicy(policy);
+            mHeader.findViewById(R.id.filter_settings).setVisibility(View.VISIBLE);
+            mHeader.findViewById(R.id.filter_settings).setOnClickListener(
+                    new View.OnClickListener() {
+                @Override
+                public void onClick(View v) {
+                    Bundle args = new Bundle();
+                    args.putParcelable(DataUsageList.EXTRA_NETWORK_TEMPLATE, mTemplate);
+                    startFragment(DataUsageList.this, BillingCycleSettings.class.getName(),
+                            R.string.billing_cycle, 0, args);
+                }
+            });
+        } else {
+            // controls are disabled; don't bind warning/limit sweeps
+            mChart.bindNetworkPolicy(null);
+            mHeader.findViewById(R.id.filter_settings).setVisibility(View.GONE);
+        }
+
+        if (refreshCycle) {
+            // generate cycle list based on policy and available history
+            if (mCycleAdapter.updateCycleList(policy, mChartData)) {
+                updateDetailData();
+            }
+        }
+    }
+
+    /**
+     * Update details based on {@link #mChart} inspection range depending on
+     * current mode. Updates {@link #mAdapter} with sorted list
+     * of applications data usage.
+     */
+    private void updateDetailData() {
+        if (LOGD) Log.d(TAG, "updateDetailData()");
+
+        final long start = mChart.getInspectStart();
+        final long end = mChart.getInspectEnd();
+        final long now = System.currentTimeMillis();
+
+        final Context context = getActivity();
+
+        NetworkStatsHistory.Entry entry = null;
+        if (mChartData != null) {
+            entry = mChartData.network.getValues(start, end, now, null);
+        }
+
+        // kick off loader for detailed stats
+        getLoaderManager().restartLoader(LOADER_SUMMARY,
+                SummaryForAllUidLoader.buildArgs(mTemplate, start, end), mSummaryCallbacks);
+
+        final long totalBytes = entry != null ? entry.rxBytes + entry.txBytes : 0;
+        final String totalPhrase = Formatter.formatFileSize(context, totalBytes);
+        mUsageAmount.setTitle(getString(R.string.data_used_template, totalPhrase));
+    }
+
+    /**
+     * Bind the given {@link NetworkStats}, or {@code null} to clear list.
+     */
+    public void bindStats(NetworkStats stats, int[] restrictedUids) {
+        ArrayList<AppItem> items = new ArrayList<>();
+        long largest = 0;
+
+        final int currentUserId = ActivityManager.getCurrentUser();
+        UserManager userManager = UserManager.get(getContext());
+        final List<UserHandle> profiles = userManager.getUserProfiles();
+        final SparseArray<AppItem> knownItems = new SparseArray<AppItem>();
+
+        NetworkStats.Entry entry = null;
+        final int size = stats != null ? stats.size() : 0;
+        for (int i = 0; i < size; i++) {
+            entry = stats.getValues(i, entry);
+
+            // Decide how to collapse items together
+            final int uid = entry.uid;
+
+            final int collapseKey;
+            final int category;
+            final int userId = UserHandle.getUserId(uid);
+            if (UserHandle.isApp(uid)) {
+                if (profiles.contains(new UserHandle(userId))) {
+                    if (userId != currentUserId) {
+                        // Add to a managed user item.
+                        final int managedKey = UidDetailProvider.buildKeyForUser(userId);
+                        largest = accumulate(managedKey, knownItems, entry, AppItem.CATEGORY_USER,
+                                items, largest);
+                    }
+                    // Add to app item.
+                    collapseKey = uid;
+                    category = AppItem.CATEGORY_APP;
+                } else {
+                    // If it is a removed user add it to the removed users' key
+                    final UserInfo info = userManager.getUserInfo(userId);
+                    if (info == null) {
+                        collapseKey = UID_REMOVED;
+                        category = AppItem.CATEGORY_APP;
+                    } else {
+                        // Add to other user item.
+                        collapseKey = UidDetailProvider.buildKeyForUser(userId);
+                        category = AppItem.CATEGORY_USER;
+                    }
+                }
+            } else if (uid == UID_REMOVED || uid == UID_TETHERING) {
+                collapseKey = uid;
+                category = AppItem.CATEGORY_APP;
+            } else {
+                collapseKey = android.os.Process.SYSTEM_UID;
+                category = AppItem.CATEGORY_APP;
+            }
+            largest = accumulate(collapseKey, knownItems, entry, category, items, largest);
+        }
+
+        final int restrictedUidsMax = restrictedUids.length;
+        for (int i = 0; i < restrictedUidsMax; ++i) {
+            final int uid = restrictedUids[i];
+            // Only splice in restricted state for current user or managed users
+            if (!profiles.contains(new UserHandle(UserHandle.getUserId(uid)))) {
+                continue;
+            }
+
+            AppItem item = knownItems.get(uid);
+            if (item == null) {
+                item = new AppItem(uid);
+                item.total = -1;
+                items.add(item);
+                knownItems.put(item.key, item);
+            }
+            item.restricted = true;
+        }
+
+        Collections.sort(items);
+        mApps.removeAll();
+        for (int i = 0; i < items.size(); i++) {
+            final int percentTotal = largest != 0 ? (int) (items.get(i).total * 100 / largest) : 0;
+            AppDataUsagePreference preference = new AppDataUsagePreference(getContext(),
+                    items.get(i), percentTotal, mUidDetailProvider);
+            preference.setOnPreferenceClickListener(new Preference.OnPreferenceClickListener() {
+                @Override
+                public boolean onPreferenceClick(Preference preference) {
+                    AppDataUsagePreference pref = (AppDataUsagePreference) preference;
+                    AppItem item = pref.getItem();
+                    startAppDataUsage(item);
+                    return true;
+                }
+            });
+            mApps.addPreference(preference);
+        }
+    }
+
+    private void startAppDataUsage(AppItem item) {
+        Bundle args = new Bundle();
+        args.putParcelable(AppDataUsage.ARG_APP_ITEM, item);
+        args.putParcelable(AppDataUsage.ARG_NETWORK_TEMPLATE, mTemplate);
+        startFragment(this, AppDataUsage.class.getName(), R.string.app_data_usage, 0, args);
+    }
+
+    /**
+     * Accumulate data usage of a network stats entry for the item mapped by the collapse key.
+     * Creates the item if needed.
+     * @param collapseKey the collapse key used to map the item.
+     * @param knownItems collection of known (already existing) items.
+     * @param entry the network stats entry to extract data usage from.
+     * @param itemCategory the item is categorized on the list view by this category. Must be
+     */
+    private static long accumulate(int collapseKey, final SparseArray<AppItem> knownItems,
+            NetworkStats.Entry entry, int itemCategory, ArrayList<AppItem> items, long largest) {
+        final int uid = entry.uid;
+        AppItem item = knownItems.get(collapseKey);
+        if (item == null) {
+            item = new AppItem(collapseKey);
+            item.category = itemCategory;
+            items.add(item);
+            knownItems.put(item.key, item);
+        }
+        item.addUid(uid);
+        item.total += entry.rxBytes + entry.txBytes;
+        return Math.max(largest, item.total);
+    }
+
+    /**
+     * Test if device has a mobile data radio with SIM in ready state.
+     */
+    public static boolean hasReadyMobileRadio(Context context) {
+        if (TEST_RADIOS) {
+            return SystemProperties.get(TEST_RADIOS_PROP).contains("mobile");
+        }
+
+        final ConnectivityManager conn = ConnectivityManager.from(context);
+        final TelephonyManager tele = TelephonyManager.from(context);
+
+        final List<SubscriptionInfo> subInfoList =
+                SubscriptionManager.from(context).getActiveSubscriptionInfoList();
+        // No activated Subscriptions
+        if (subInfoList == null) {
+            if (LOGD) Log.d(TAG, "hasReadyMobileRadio: subInfoList=null");
+            return false;
+        }
+        // require both supported network and ready SIM
+        boolean isReady = true;
+        for (SubscriptionInfo subInfo : subInfoList) {
+            isReady = isReady & tele.getSimState(subInfo.getSimSlotIndex()) == SIM_STATE_READY;
+            if (LOGD) Log.d(TAG, "hasReadyMobileRadio: subInfo=" + subInfo);
+        }
+        boolean retVal = conn.isNetworkSupported(TYPE_MOBILE) && isReady;
+        if (LOGD) {
+            Log.d(TAG, "hasReadyMobileRadio:"
+                    + " conn.isNetworkSupported(TYPE_MOBILE)="
+                                            + conn.isNetworkSupported(TYPE_MOBILE)
+                    + " isReady=" + isReady);
+        }
+        return retVal;
+    }
+
+    /*
+     * TODO: consider adding to TelephonyManager or SubscriptionManager.
+     */
+    public static boolean hasReadyMobileRadio(Context context, int subId) {
+        if (TEST_RADIOS) {
+            return SystemProperties.get(TEST_RADIOS_PROP).contains("mobile");
+        }
+
+        final ConnectivityManager conn = ConnectivityManager.from(context);
+        final TelephonyManager tele = TelephonyManager.from(context);
+        final int slotId = SubscriptionManager.getSlotId(subId);
+        final boolean isReady = tele.getSimState(slotId) == SIM_STATE_READY;
+
+        boolean retVal =  conn.isNetworkSupported(TYPE_MOBILE) && isReady;
+        if (LOGD) Log.d(TAG, "hasReadyMobileRadio: subId=" + subId
+                + " conn.isNetworkSupported(TYPE_MOBILE)=" + conn.isNetworkSupported(TYPE_MOBILE)
+                + " isReady=" + isReady);
+        return retVal;
+    }
+
+    private OnItemSelectedListener mCycleListener = new OnItemSelectedListener() {
+        @Override
+        public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
+            final CycleAdapter.CycleItem cycle = (CycleAdapter.CycleItem)
+                    parent.getItemAtPosition(position);
+
+            if (LOGD) {
+                Log.d(TAG, "showing cycle " + cycle + ", start=" + cycle.start + ", end="
+                        + cycle.end + "]");
+            }
+
+            // update chart to show selected cycle, and update detail data
+            // to match updated sweep bounds.
+            mChart.setVisibleRange(cycle.start, cycle.end);
+
+            updateDetailData();
+        }
+
+        @Override
+        public void onNothingSelected(AdapterView<?> parent) {
+            // ignored
+        }
+    };
+
+    private final LoaderCallbacks<ChartData> mChartDataCallbacks = new LoaderCallbacks<
+            ChartData>() {
+        @Override
+        public Loader<ChartData> onCreateLoader(int id, Bundle args) {
+            return new ChartDataLoader(getActivity(), mStatsSession, args);
+        }
+
+        @Override
+        public void onLoadFinished(Loader<ChartData> loader, ChartData data) {
+            setLoading(false, true);
+            mChartData = data;
+            mChart.bindNetworkStats(mChartData.network);
+
+            // calcuate policy cycles based on available data
+            updatePolicy(true);
+        }
+
+        @Override
+        public void onLoaderReset(Loader<ChartData> loader) {
+            mChartData = null;
+            mChart.bindNetworkStats(null);
+        }
+    };
+
+    private final LoaderCallbacks<NetworkStats> mSummaryCallbacks = new LoaderCallbacks<
+            NetworkStats>() {
+        @Override
+        public Loader<NetworkStats> onCreateLoader(int id, Bundle args) {
+            return new SummaryForAllUidLoader(getActivity(), mStatsSession, args);
+        }
+
+        @Override
+        public void onLoadFinished(Loader<NetworkStats> loader, NetworkStats data) {
+            final int[] restrictedUids = services.mPolicyManager.getUidsWithPolicy(
+                    POLICY_REJECT_METERED_BACKGROUND);
+            bindStats(data, restrictedUids);
+            updateEmptyVisible();
+        }
+
+        @Override
+        public void onLoaderReset(Loader<NetworkStats> loader) {
+            bindStats(null, new int[0]);
+            updateEmptyVisible();
+        }
+
+        private void updateEmptyVisible() {
+            if ((mApps.getPreferenceCount() != 0) !=
+                    (getPreferenceScreen().getPreferenceCount() != 0)) {
+                if (mApps.getPreferenceCount() != 0) {
+                    getPreferenceScreen().addPreference(mUsageAmount);
+                    getPreferenceScreen().addPreference(mApps);
+                } else {
+                    getPreferenceScreen().removeAll();
+                }
+            }
+        }
+    };
+}
diff --git a/src/com/android/settings/net/DataUsageMeteredSettings.java b/src/com/android/settings/datausage/DataUsageMeteredSettings.java
similarity index 93%
rename from src/com/android/settings/net/DataUsageMeteredSettings.java
rename to src/com/android/settings/datausage/DataUsageMeteredSettings.java
index ef3a705..9790704 100644
--- a/src/com/android/settings/net/DataUsageMeteredSettings.java
+++ b/src/com/android/settings/datausage/DataUsageMeteredSettings.java
@@ -1,20 +1,18 @@
 /*
- * Copyright (C) 2012 The Android Open Source Project
+ * Copyright (C) 2016 The Android Open Source Project
  *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of the License at
  *
  *      http://www.apache.org/licenses/LICENSE-2.0
  *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
+ * Unless required by applicable law or agreed to in writing, software distributed under the
+ * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the specific language governing
+ * permissions and limitations under the License.
  */
 
-package com.android.settings.net;
+package com.android.settings.datausage;
 
 import android.content.Context;
 import android.content.res.Resources;
@@ -42,8 +40,8 @@
 
 import static android.net.NetworkPolicy.LIMIT_DISABLED;
 import static android.net.wifi.WifiInfo.removeDoubleQuotes;
-import static com.android.settings.DataUsageSummary.hasReadyMobileRadio;
-import static com.android.settings.DataUsageSummary.hasWifiRadio;
+import static com.android.settings.datausage.DataUsageList.hasReadyMobileRadio;
+import static com.android.settings.datausage.DataUsageSummary.hasWifiRadio;
 
 /**
  * Panel to configure {@link NetworkPolicy#metered} for networks.
diff --git a/src/com/android/settings/datausage/DataUsagePreference.java b/src/com/android/settings/datausage/DataUsagePreference.java
new file mode 100644
index 0000000..bd865b5
--- /dev/null
+++ b/src/com/android/settings/datausage/DataUsagePreference.java
@@ -0,0 +1,58 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the
+ * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the specific language governing
+ * permissions and limitations under the License.
+ */
+
+package com.android.settings.datausage;
+
+import android.content.Context;
+import android.content.Intent;
+import android.net.NetworkTemplate;
+import android.os.Bundle;
+import android.support.v7.preference.Preference;
+import android.telephony.SubscriptionManager;
+import android.text.format.Formatter;
+import android.util.AttributeSet;
+import com.android.settings.Utils;
+import com.android.settingslib.net.DataUsageController;
+import com.android.settings.R;
+
+public class DataUsagePreference extends Preference implements TemplatePreference {
+
+    private NetworkTemplate mTemplate;
+    private int mSubId;
+
+    public DataUsagePreference(Context context, AttributeSet attrs) {
+        super(context, attrs);
+    }
+
+    @Override
+    public void setTemplate(NetworkTemplate template, int subId,
+            NetworkServices services) {
+        mTemplate = template;
+        mSubId = subId;
+        DataUsageController controller = new DataUsageController(getContext());
+        DataUsageController.DataUsageInfo usageInfo = controller.getDataUsageInfo(mTemplate);
+        setSummary(getContext().getString(R.string.data_usage_template,
+                Formatter.formatFileSize(getContext(), usageInfo.usageLevel), usageInfo.period));
+        setIntent(getIntent());
+    }
+
+    @Override
+    public Intent getIntent() {
+        Bundle args = new Bundle();
+        args.putParcelable(DataUsageList.EXTRA_NETWORK_TEMPLATE, mTemplate);
+        args.putInt(DataUsageList.EXTRA_SUB_ID, mSubId);
+        return Utils.onBuildStartFragmentIntent(getContext(), DataUsageList.class.getName(), args,
+                getContext().getPackageName(), 0, getTitle(), false);
+    }
+}
diff --git a/src/com/android/settings/datausage/DataUsageSummary.java b/src/com/android/settings/datausage/DataUsageSummary.java
new file mode 100644
index 0000000..de98008
--- /dev/null
+++ b/src/com/android/settings/datausage/DataUsageSummary.java
@@ -0,0 +1,375 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the
+ * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the specific language governing
+ * permissions and limitations under the License.
+ */
+
+package com.android.settings.datausage;
+
+import android.app.Activity;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.net.ConnectivityManager;
+import android.net.INetworkStatsSession;
+import android.net.NetworkTemplate;
+import android.net.TrafficStats;
+import android.os.Bundle;
+import android.os.RemoteException;
+import android.os.SystemProperties;
+import android.provider.SearchIndexableResource;
+import android.support.v7.preference.Preference;
+import android.support.v7.preference.PreferenceScreen;
+import android.telephony.SubscriptionInfo;
+import android.telephony.SubscriptionManager;
+import android.telephony.TelephonyManager;
+import android.text.format.Formatter;
+import android.view.Menu;
+import android.view.MenuInflater;
+import android.view.MenuItem;
+import com.android.internal.logging.MetricsLogger;
+import com.android.settings.R;
+import com.android.settings.SummaryPreference;
+import com.android.settings.Utils;
+import com.android.settings.dashboard.SummaryLoader;
+import com.android.settings.search.BaseSearchIndexProvider;
+import com.android.settings.search.Indexable;
+import com.android.settingslib.net.DataUsageController;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import static android.net.ConnectivityManager.TYPE_ETHERNET;
+import static android.net.ConnectivityManager.TYPE_WIFI;
+
+public class DataUsageSummary extends DataUsageBase implements Indexable {
+
+    private static final String TAG = "DataUsageSummary";
+    static final boolean LOGD = false;
+
+    public static final boolean TEST_RADIOS = false;
+    public static final String TEST_RADIOS_PROP = "test.radios";
+
+    private static final String KEY_STATUS_HEADER = "status_header";
+    private static final String KEY_LIMIT_SUMMARY = "limit_summary";
+    private static final String KEY_RESTRICT_BACKGROUND = "restrict_background";
+
+    private DataUsageController mDataUsageController;
+    private SummaryPreference mSummaryPreference;
+    private Preference mLimitPreference;
+    private NetworkTemplate mDefaultTemplate;
+    private int mDataUsageTemplate;
+
+    @Override
+    public void onCreate(Bundle icicle) {
+        super.onCreate(icicle);
+
+        boolean hasMobileData = hasMobileData(getContext());
+        mDataUsageController = new DataUsageController(getContext());
+        addPreferencesFromResource(R.xml.data_usage);
+
+        int defaultSubId = getDefaultSubscriptionId(getContext());
+        mDefaultTemplate = getDefaultTemplate(getContext(), defaultSubId);
+        if (hasMobileData) {
+            mLimitPreference = findPreference(KEY_LIMIT_SUMMARY);
+        } else {
+            removePreference(KEY_LIMIT_SUMMARY);
+        }
+        if (!hasMobileData || !isAdmin()) {
+            removePreference(KEY_RESTRICT_BACKGROUND);
+        }
+        if (hasMobileData) {
+            List<SubscriptionInfo> subscriptions =
+                    services.mSubscriptionManager.getActiveSubscriptionInfoList();
+            if (subscriptions.size() == 0) {
+                addMobileSection(defaultSubId);
+            }
+            for (int i = 0; i < subscriptions.size(); i++) {
+                addMobileSection(subscriptions.get(i).getSubscriptionId());
+            }
+        }
+        boolean hasWifiRadio = hasWifiRadio(getContext());
+        if (hasWifiRadio) {
+            addWifiSection();
+        }
+        if (hasEthernet(getContext())) {
+            addEthernetSection();
+        }
+        mDataUsageTemplate = hasMobileData ? R.string.cell_data_template
+                : hasWifiRadio ? R.string.wifi_data_template
+                : R.string.ethernet_data_template;
+
+        mSummaryPreference = (SummaryPreference) findPreference(KEY_STATUS_HEADER);
+        setHasOptionsMenu(true);
+    }
+
+    @Override
+    public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
+        inflater.inflate(R.menu.data_usage, menu);
+        super.onCreateOptionsMenu(menu, inflater);
+    }
+
+    @Override
+    public boolean onOptionsItemSelected(MenuItem item) {
+        switch (item.getItemId()) {
+            case R.id.data_usage_menu_cellular_networks: {
+                final Intent intent = new Intent(Intent.ACTION_MAIN);
+                intent.setComponent(new ComponentName("com.android.phone",
+                        "com.android.phone.MobileNetworkSettings"));
+                startActivity(intent);
+                return true;
+            }
+        }
+        return false;
+    }
+
+    private void addMobileSection(int subId) {
+        TemplatePreferenceCategory category = (TemplatePreferenceCategory)
+                inflatePreferences(R.xml.data_usage_cellular);
+        category.setTemplate(getNetworkTemplate(subId), subId, services);
+    }
+
+    private void addWifiSection() {
+        TemplatePreferenceCategory category = (TemplatePreferenceCategory)
+                inflatePreferences(R.xml.data_usage_wifi);
+        category.setTemplate(NetworkTemplate.buildTemplateWifiWildcard(), 0, services);
+    }
+
+    private void addEthernetSection() {
+        TemplatePreferenceCategory category = (TemplatePreferenceCategory)
+                inflatePreferences(R.xml.data_usage_ethernet);
+        category.setTemplate(NetworkTemplate.buildTemplateEthernet(), 0, services);
+    }
+
+    private Preference inflatePreferences(int resId) {
+        PreferenceScreen rootPreferences = getPreferenceManager().inflateFromResource(
+                getPrefContext(), resId, null);
+        Preference pref = rootPreferences.getPreference(0);
+        rootPreferences.removeAll();
+
+        PreferenceScreen screen = getPreferenceScreen();
+        pref.setOrder(screen.getPreferenceCount());
+        screen.addPreference(pref);
+
+        return pref;
+    }
+
+    private NetworkTemplate getNetworkTemplate(int subscriptionId) {
+        NetworkTemplate mobileAll = NetworkTemplate.buildTemplateMobileAll(
+                services.mTelephonyManager.getSubscriberId(subscriptionId));
+        return NetworkTemplate.normalize(mobileAll,
+                services.mTelephonyManager.getMergedSubscriberIds());
+    }
+
+    @Override
+    public void onResume() {
+        super.onResume();
+        updateState();
+    }
+
+    private void updateState() {
+        DataUsageController.DataUsageInfo info = mDataUsageController.getDataUsageInfo(
+                mDefaultTemplate);
+        Context context = getContext();
+        if (mSummaryPreference != null) {
+            Formatter.BytesResult usedResult = Formatter.formatBytes(context.getResources(),
+                    info.usageLevel, Formatter.FLAG_SHORTER);
+            mSummaryPreference.setAmount(usedResult.value);
+            mSummaryPreference.setUnits(getString(mDataUsageTemplate, usedResult.units));
+            long limit = info.limitLevel;
+            if (limit <= 0) {
+                limit = info.warningLevel;
+            }
+            if (info.usageLevel > limit) {
+                limit = info.usageLevel;
+            }
+            mSummaryPreference.setSummary(info.period);
+            mSummaryPreference.setLabels(Formatter.formatFileSize(context, 0),
+                    Formatter.formatFileSize(context, limit));
+            mSummaryPreference.setRatios(info.usageLevel / (float) limit, 0,
+                    (limit - info.usageLevel) / (float) limit);
+        }
+        if (mLimitPreference != null) {
+            String warning = Formatter.formatFileSize(context, info.warningLevel);
+            String limit = Formatter.formatFileSize(context, info.limitLevel);
+            mLimitPreference.setSummary(getString(info.limitLevel <= 0 ? R.string.cell_warning_only
+                    : R.string.cell_warning_and_limit, warning, limit));
+        }
+
+        PreferenceScreen screen = getPreferenceScreen();
+        for (int i = 1; i < screen.getPreferenceCount(); i++) {
+            ((TemplatePreferenceCategory) screen.getPreference(i)).pushTemplates(services);
+        }
+    }
+
+    @Override
+    protected int getMetricsCategory() {
+        return MetricsLogger.DATA_USAGE_SUMMARY;
+    }
+
+    /**
+     * Test if device has an ethernet network connection.
+     */
+    public boolean hasEthernet(Context context) {
+        if (TEST_RADIOS) {
+            return SystemProperties.get(TEST_RADIOS_PROP).contains("ethernet");
+        }
+
+        final ConnectivityManager conn = ConnectivityManager.from(context);
+        final boolean hasEthernet = conn.isNetworkSupported(TYPE_ETHERNET);
+
+        final long ethernetBytes;
+        try {
+            INetworkStatsSession statsSession = services.mStatsService.openSession();
+            if (statsSession != null) {
+                ethernetBytes = statsSession.getSummaryForNetwork(
+                        NetworkTemplate.buildTemplateEthernet(), Long.MIN_VALUE, Long.MAX_VALUE)
+                        .getTotalBytes();
+                TrafficStats.closeQuietly(statsSession);
+            } else {
+                ethernetBytes = 0;
+            }
+        } catch (RemoteException e) {
+            throw new RuntimeException(e);
+        }
+
+        // only show ethernet when both hardware present and traffic has occurred
+        return hasEthernet && ethernetBytes > 0;
+    }
+
+    public static boolean hasMobileData(Context context) {
+        return ConnectivityManager.from(context).isNetworkSupported(
+                ConnectivityManager.TYPE_MOBILE);
+    }
+
+    /**
+     * Test if device has a Wi-Fi data radio.
+     */
+    public static boolean hasWifiRadio(Context context) {
+        if (TEST_RADIOS) {
+            return SystemProperties.get(TEST_RADIOS_PROP).contains("wifi");
+        }
+
+        final ConnectivityManager conn = ConnectivityManager.from(context);
+        return conn.isNetworkSupported(TYPE_WIFI);
+    }
+
+    public static int getDefaultSubscriptionId(Context context) {
+        SubscriptionManager subManager = SubscriptionManager.from(context);
+        if (subManager == null) {
+            return SubscriptionManager.INVALID_SUBSCRIPTION_ID;
+        }
+        SubscriptionInfo subscriptionInfo = subManager.getDefaultDataSubscriptionInfo();
+        if (subscriptionInfo == null) {
+            List<SubscriptionInfo> list = subManager.getAllSubscriptionInfoList();
+            if (list.size() == 0) {
+                return SubscriptionManager.INVALID_SUBSCRIPTION_ID;
+            }
+            subscriptionInfo = list.get(0);
+        }
+        return subscriptionInfo.getSubscriptionId();
+    }
+
+    public static NetworkTemplate getDefaultTemplate(Context context, int defaultSubId) {
+        if (hasMobileData(context)) {
+            TelephonyManager telephonyManager = TelephonyManager.from(context);
+            NetworkTemplate mobileAll = NetworkTemplate.buildTemplateMobileAll(
+                    telephonyManager.getSubscriberId(defaultSubId));
+            return NetworkTemplate.normalize(mobileAll,
+                    telephonyManager.getMergedSubscriberIds());
+        } else if (hasWifiRadio(context)) {
+            return NetworkTemplate.buildTemplateWifiWildcard();
+        } else {
+            return NetworkTemplate.buildTemplateEthernet();
+        }
+    }
+
+    private static class SummaryProvider
+            implements SummaryLoader.SummaryProvider {
+
+        private final Activity mActivity;
+        private final SummaryLoader mSummaryLoader;
+        private final DataUsageController mDataController;
+
+        public SummaryProvider(Activity activity, SummaryLoader summaryLoader) {
+            mActivity = activity;
+            mSummaryLoader = summaryLoader;
+            mDataController = new DataUsageController(activity);
+        }
+
+        @Override
+        public void setListening(boolean listening) {
+            if (listening) {
+                DataUsageController.DataUsageInfo info = mDataController.getDataUsageInfo();
+                String used;
+                if (info == null) {
+                    used = Formatter.formatFileSize(mActivity, 0);
+                } else if (info.limitLevel <= 0) {
+                    used = Formatter.formatFileSize(mActivity, info.usageLevel);
+                } else {
+                    used = Utils.formatPercentage(info.usageLevel, info.limitLevel);
+                }
+                mSummaryLoader.setSummary(this,
+                        mActivity.getString(R.string.data_usage_summary_format, used));
+            }
+        }
+    }
+
+    public static final SummaryLoader.SummaryProviderFactory SUMMARY_PROVIDER_FACTORY
+            = new SummaryLoader.SummaryProviderFactory() {
+        @Override
+        public SummaryLoader.SummaryProvider createSummaryProvider(Activity activity,
+                                                                   SummaryLoader summaryLoader) {
+            return new SummaryProvider(activity, summaryLoader);
+        }
+    };
+
+    /**
+     * For search
+     */
+    public static final Indexable.SearchIndexProvider SEARCH_INDEX_DATA_PROVIDER =
+        new BaseSearchIndexProvider() {
+
+            @Override
+            public List<SearchIndexableResource> getXmlResourcesToIndex(Context context,
+                    boolean enabled) {
+                ArrayList<SearchIndexableResource> resources = new ArrayList<>();
+                SearchIndexableResource resource = new SearchIndexableResource(context);
+                resource.xmlResId = R.xml.data_usage;
+                resources.add(resource);
+
+                if (hasMobileData(context)) {
+                    resource = new SearchIndexableResource(context);
+                    resource.xmlResId = R.xml.data_usage_cellular;
+                    resources.add(resource);
+                }
+                if (hasWifiRadio(context)) {
+                    resource = new SearchIndexableResource(context);
+                    resource.xmlResId = R.xml.data_usage_wifi;
+                    resources.add(resource);
+                }
+                return resources;
+            }
+
+            @Override
+            public List<String> getNonIndexableKeys(Context context) {
+                ArrayList<String> keys = new ArrayList<>();
+                boolean hasMobileData = ConnectivityManager.from(context).isNetworkSupported(
+                        ConnectivityManager.TYPE_MOBILE);
+
+                if (hasMobileData) {
+                    keys.add(KEY_RESTRICT_BACKGROUND);
+                }
+
+                return keys;
+            }
+        };
+}
diff --git a/src/com/android/settings/datausage/NetworkRestrictionsPreference.java b/src/com/android/settings/datausage/NetworkRestrictionsPreference.java
new file mode 100644
index 0000000..e2e9d1e
--- /dev/null
+++ b/src/com/android/settings/datausage/NetworkRestrictionsPreference.java
@@ -0,0 +1,33 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the
+ * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the specific language governing
+ * permissions and limitations under the License.
+ */
+
+package com.android.settings.datausage;
+
+import android.content.Context;
+import android.net.NetworkTemplate;
+import android.support.v7.preference.Preference;
+import android.util.AttributeSet;
+
+public class NetworkRestrictionsPreference extends Preference implements TemplatePreference {
+
+    public NetworkRestrictionsPreference(Context context, AttributeSet attrs) {
+        super(context, attrs);
+    }
+
+    @Override
+    public void setTemplate(NetworkTemplate template, int subId,
+            NetworkServices services) {
+        // TODO: Summary
+    }
+}
diff --git a/src/com/android/settings/datausage/RestrictBackgroundDataPreference.java b/src/com/android/settings/datausage/RestrictBackgroundDataPreference.java
new file mode 100644
index 0000000..67bce03
--- /dev/null
+++ b/src/com/android/settings/datausage/RestrictBackgroundDataPreference.java
@@ -0,0 +1,99 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the
+ * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the specific language governing
+ * permissions and limitations under the License.
+ */
+
+package com.android.settings.datausage;
+
+import android.app.AlertDialog;
+import android.content.Context;
+import android.content.DialogInterface;
+import android.net.NetworkPolicyManager;
+import android.support.v7.preference.PreferenceViewHolder;
+import android.util.AttributeSet;
+import android.view.View;
+import android.widget.Checkable;
+import com.android.settings.CustomDialogPreference;
+import com.android.settings.R;
+import com.android.settings.Utils;
+import com.android.settings.dashboard.conditional.BackgroundDataCondition;
+import com.android.settings.dashboard.conditional.ConditionManager;
+
+public class RestrictBackgroundDataPreference extends CustomDialogPreference {
+
+    private NetworkPolicyManager mPolicyManager;
+    private boolean mChecked;
+
+    public RestrictBackgroundDataPreference(Context context, AttributeSet attrs) {
+        super(context, attrs, android.R.attr.switchPreferenceStyle);
+    }
+
+    @Override
+    public void onAttached() {
+        super.onAttached();
+        mPolicyManager = NetworkPolicyManager.from(getContext());
+        setChecked(mPolicyManager.getRestrictBackground());
+    }
+
+    public void setRestrictBackground(boolean restrictBackground) {
+        mPolicyManager.setRestrictBackground(restrictBackground);
+        setChecked(restrictBackground);
+        ConditionManager.get(getContext()).getCondition(BackgroundDataCondition.class)
+                .refreshState();
+    }
+
+    private void setChecked(boolean checked) {
+        if (mChecked == checked) return;
+        mChecked = checked;
+        notifyChanged();
+    }
+
+    @Override
+    public void onBindViewHolder(PreferenceViewHolder holder) {
+        super.onBindViewHolder(holder);
+        View switchView = holder.findViewById(android.R.id.switch_widget);
+        switchView.setClickable(false);
+        ((Checkable) switchView).setChecked(mChecked);
+    }
+
+    @Override
+    protected void performClick(View view) {
+        if (mChecked) {
+            setRestrictBackground(false);
+        } else {
+            super.performClick(view);
+        }
+    }
+
+    @Override
+    protected void onPrepareDialogBuilder(AlertDialog.Builder builder,
+            DialogInterface.OnClickListener listener) {
+        super.onPrepareDialogBuilder(builder, listener);
+        builder.setTitle(R.string.data_usage_restrict_background_title);
+        if (Utils.hasMultipleUsers(getContext())) {
+            builder.setMessage(R.string.data_usage_restrict_background_multiuser);
+        } else {
+            builder.setMessage(R.string.data_usage_restrict_background);
+        }
+
+        builder.setPositiveButton(android.R.string.ok, listener);
+        builder.setNegativeButton(android.R.string.cancel, null);
+    }
+
+    @Override
+    protected void onClick(DialogInterface dialog, int which) {
+        if (which != DialogInterface.BUTTON_POSITIVE) {
+            return;
+        }
+        setRestrictBackground(true);
+    }
+}
diff --git a/src/com/android/settings/datausage/TemplatePreference.java b/src/com/android/settings/datausage/TemplatePreference.java
new file mode 100644
index 0000000..4b1cd0c
--- /dev/null
+++ b/src/com/android/settings/datausage/TemplatePreference.java
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the
+ * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the specific language governing
+ * permissions and limitations under the License.
+ */
+
+package com.android.settings.datausage;
+
+import android.net.INetworkStatsService;
+import android.net.NetworkPolicyManager;
+import android.net.NetworkTemplate;
+import android.os.INetworkManagementService;
+import android.os.UserManager;
+import android.telephony.SubscriptionInfo;
+import android.telephony.SubscriptionManager;
+import android.telephony.TelephonyManager;
+import com.android.settingslib.NetworkPolicyEditor;
+
+public interface TemplatePreference {
+
+    void setTemplate(NetworkTemplate template, int subId, NetworkServices services);
+
+    class NetworkServices {
+        INetworkManagementService mNetworkService;
+        INetworkStatsService mStatsService;
+        NetworkPolicyManager mPolicyManager;
+        TelephonyManager mTelephonyManager;
+        SubscriptionManager mSubscriptionManager;
+        UserManager mUserManager;
+        NetworkPolicyEditor mPolicyEditor;
+    }
+
+}
diff --git a/src/com/android/settings/datausage/TemplatePreferenceCategory.java b/src/com/android/settings/datausage/TemplatePreferenceCategory.java
new file mode 100644
index 0000000..0be5c73
--- /dev/null
+++ b/src/com/android/settings/datausage/TemplatePreferenceCategory.java
@@ -0,0 +1,57 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the
+ * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the specific language governing
+ * permissions and limitations under the License.
+ */
+
+package com.android.settings.datausage;
+
+import android.content.Context;
+import android.net.NetworkTemplate;
+import android.support.v7.preference.Preference;
+import android.support.v7.preference.PreferenceCategory;
+import android.util.AttributeSet;
+
+public class TemplatePreferenceCategory extends PreferenceCategory implements TemplatePreference {
+
+    private NetworkTemplate mTemplate;
+    private int mSubId;
+
+    public TemplatePreferenceCategory(Context context, AttributeSet attrs) {
+        super(context, attrs);
+    }
+
+    @Override
+    public void setTemplate(NetworkTemplate template, int subId,
+            NetworkServices services) {
+        mTemplate = template;
+        mSubId = subId;
+    }
+
+    @Override
+    public boolean addPreference(Preference preference) {
+        if (!(preference instanceof TemplatePreference)) {
+            throw new IllegalArgumentException(
+                    "TemplatePreferenceCategories can only hold TemplatePreferences");
+        }
+        return super.addPreference(preference);
+    }
+
+    public void pushTemplates(NetworkServices services) {
+        if (mTemplate == null) {
+            throw new RuntimeException("null mTemplate for " + getKey());
+        }
+        for (int i = 0; i < getPreferenceCount(); i++) {
+            ((TemplatePreference) getPreference(i)).setTemplate(mTemplate, mSubId, services);
+        }
+    }
+
+}
diff --git a/src/com/android/settings/search/Ranking.java b/src/com/android/settings/search/Ranking.java
index 3d5ff35..59f83ec 100644
--- a/src/com/android/settings/search/Ranking.java
+++ b/src/com/android/settings/search/Ranking.java
@@ -17,7 +17,6 @@
 package com.android.settings.search;
 
 import com.android.settings.ChooseLockGeneric;
-import com.android.settings.DataUsageSummary;
 import com.android.settings.DateTimeSettings;
 import com.android.settings.DevelopmentSettings;
 import com.android.settings.DeviceInfoSettings;
@@ -35,6 +34,8 @@
 import com.android.settings.applications.AdvancedAppSettings;
 import com.android.settings.applications.ManageDefaultApps;
 import com.android.settings.bluetooth.BluetoothSettings;
+import com.android.settings.datausage.DataUsageMeteredSettings;
+import com.android.settings.datausage.DataUsageSummary;
 import com.android.settings.deviceinfo.StorageSettings;
 import com.android.settings.display.ScreenZoomSettings;
 import com.android.settings.fuelgauge.BatterySaverSettings;
@@ -42,7 +43,6 @@
 import com.android.settings.inputmethod.InputMethodAndLanguageSettings;
 import com.android.settings.location.LocationSettings;
 import com.android.settings.location.ScanningSettings;
-import com.android.settings.net.DataUsageMeteredSettings;
 import com.android.settings.notification.ConfigureNotificationSettings;
 import com.android.settings.notification.OtherSoundSettings;
 import com.android.settings.notification.SoundSettings;
diff --git a/src/com/android/settings/search/SearchIndexableResources.java b/src/com/android/settings/search/SearchIndexableResources.java
index f15ff63..1d55b55 100644
--- a/src/com/android/settings/search/SearchIndexableResources.java
+++ b/src/com/android/settings/search/SearchIndexableResources.java
@@ -16,9 +16,6 @@
 
 package com.android.settings.search;
 
-import android.provider.SearchIndexableResource;
-
-import com.android.settings.DataUsageSummary;
 import com.android.settings.DateTimeSettings;
 import com.android.settings.DevelopmentSettings;
 import com.android.settings.DeviceInfoSettings;
@@ -37,6 +34,8 @@
 import com.android.settings.applications.AdvancedAppSettings;
 import com.android.settings.applications.ManageDefaultApps;
 import com.android.settings.bluetooth.BluetoothSettings;
+import com.android.settings.datausage.DataUsageMeteredSettings;
+import com.android.settings.datausage.DataUsageSummary;
 import com.android.settings.deviceinfo.StorageSettings;
 import com.android.settings.display.ScreenZoomSettings;
 import com.android.settings.fuelgauge.BatterySaverSettings;
@@ -44,7 +43,6 @@
 import com.android.settings.inputmethod.InputMethodAndLanguageSettings;
 import com.android.settings.location.LocationSettings;
 import com.android.settings.location.ScanningSettings;
-import com.android.settings.net.DataUsageMeteredSettings;
 import com.android.settings.notification.ConfigureNotificationSettings;
 import com.android.settings.notification.OtherSoundSettings;
 import com.android.settings.notification.SoundSettings;
@@ -58,6 +56,8 @@
 import com.android.settings.wifi.SavedAccessPointsWifiSettings;
 import com.android.settings.wifi.WifiSettings;
 
+import android.provider.SearchIndexableResource;
+
 import java.util.Collection;
 import java.util.HashMap;
 
diff --git a/src/com/android/settings/widget/ChartGridView.java b/src/com/android/settings/widget/ChartGridView.java
index c85d4fc..2091719 100644
--- a/src/com/android/settings/widget/ChartGridView.java
+++ b/src/com/android/settings/widget/ChartGridView.java
@@ -32,7 +32,7 @@
 import com.android.internal.util.Preconditions;
 import com.android.settings.R;
 
-import static com.android.settings.DataUsageSummary.formatDateRange;
+import static com.android.settings.Utils.formatDateRange;
 
 /**
  * Background of {@link ChartView} that renders grid lines as requested by
diff --git a/src/com/android/settings/widget/ChartSweepView.java b/src/com/android/settings/widget/ChartSweepView.java
index e8e24aa..cdcd0a7 100644
--- a/src/com/android/settings/widget/ChartSweepView.java
+++ b/src/com/android/settings/widget/ChartSweepView.java
@@ -143,7 +143,6 @@
         a.recycle();
 
         setClickable(true);
-        setFocusable(true);
         setOnClickListener(mClickListener);
 
         setWillNotDraw(false);