OSDN Git Service

Merge tag 'android-6.0.1_r74' into HEAD
[android-x86/packages-apps-Settings.git] / src / com / android / settings / DataUsageSummary.java
1 /*
2  * Copyright (C) 2011 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16
17 package com.android.settings;
18
19 import static android.net.ConnectivityManager.TYPE_ETHERNET;
20 import static android.net.ConnectivityManager.TYPE_MOBILE;
21 import static android.net.ConnectivityManager.TYPE_WIFI;
22 import static android.net.ConnectivityManager.TYPE_WIMAX;
23 import static android.net.NetworkPolicy.LIMIT_DISABLED;
24 import static android.net.NetworkPolicy.WARNING_DISABLED;
25 import static android.net.NetworkPolicyManager.EXTRA_NETWORK_TEMPLATE;
26 import static android.net.NetworkPolicyManager.POLICY_NONE;
27 import static android.net.NetworkPolicyManager.POLICY_REJECT_METERED_BACKGROUND;
28 import static android.net.NetworkPolicyManager.POLICY_REJECT_ON_WLAN_BACKGROUND;
29 import static android.net.NetworkPolicyManager.POLICY_REJECT_ON_DATA;
30 import static android.net.NetworkPolicyManager.computeLastCycleBoundary;
31 import static android.net.NetworkPolicyManager.computeNextCycleBoundary;
32 import static android.net.NetworkTemplate.MATCH_MOBILE_3G_LOWER;
33 import static android.net.NetworkTemplate.MATCH_MOBILE_4G;
34 import static android.net.NetworkTemplate.MATCH_MOBILE_ALL;
35 import static android.net.NetworkTemplate.MATCH_WIFI;
36 import static android.net.NetworkTemplate.buildTemplateEthernet;
37 import static android.net.NetworkTemplate.buildTemplateMobile3gLower;
38 import static android.net.NetworkTemplate.buildTemplateMobile4g;
39 import static android.net.NetworkTemplate.buildTemplateMobileAll;
40 import static android.net.NetworkTemplate.buildTemplateWifiWildcard;
41 import static android.net.TrafficStats.GB_IN_BYTES;
42 import static android.net.TrafficStats.MB_IN_BYTES;
43 import static android.net.TrafficStats.UID_REMOVED;
44 import static android.net.TrafficStats.UID_TETHERING;
45 import static android.telephony.TelephonyManager.SIM_STATE_READY;
46 import static android.text.format.DateUtils.FORMAT_ABBREV_MONTH;
47 import static android.text.format.DateUtils.FORMAT_SHOW_DATE;
48 import static android.view.ViewGroup.LayoutParams.WRAP_CONTENT;
49 import static com.android.internal.util.Preconditions.checkNotNull;
50 import static com.android.settings.Utils.prepareCustomPreferencesList;
51
52 import android.animation.LayoutTransition;
53 import android.app.ActivityManager;
54 import android.app.AlertDialog;
55 import android.app.Dialog;
56 import android.app.DialogFragment;
57 import android.app.Fragment;
58 import android.app.FragmentTransaction;
59 import android.app.LoaderManager.LoaderCallbacks;
60 import android.content.ComponentName;
61 import android.content.Context;
62 import android.content.DialogInterface;
63 import android.content.Intent;
64 import android.content.Loader;
65 import android.content.SharedPreferences;
66 import android.content.pm.PackageManager;
67 import android.content.pm.PackageManager.NameNotFoundException;
68 import android.content.pm.UserInfo;
69 import android.content.res.Resources;
70 import android.graphics.Color;
71 import android.graphics.drawable.ColorDrawable;
72 import android.graphics.drawable.Drawable;
73 import android.net.ConnectivityManager;
74 import android.net.INetworkPolicyManager;
75 import android.net.INetworkStatsService;
76 import android.net.INetworkStatsSession;
77 import android.net.NetworkPolicy;
78 import android.net.NetworkPolicyManager;
79 import android.net.NetworkStats;
80 import android.net.NetworkStatsHistory;
81 import android.net.NetworkTemplate;
82 import android.net.TrafficStats;
83 import android.os.AsyncTask;
84 import android.os.Bundle;
85 import android.os.INetworkManagementService;
86 import android.os.RemoteException;
87 import android.os.ServiceManager;
88 import android.os.SystemProperties;
89 import android.os.UserHandle;
90 import android.os.UserManager;
91 import android.preference.Preference;
92 import android.telephony.SubscriptionInfo;
93 import android.telephony.SubscriptionManager;
94 import android.telephony.TelephonyManager;
95 import android.text.TextUtils;
96 import android.text.format.DateUtils;
97 import android.text.format.Formatter;
98 import android.text.format.Time;
99 import android.util.Log;
100 import android.util.SparseArray;
101 import android.view.LayoutInflater;
102 import android.view.Menu;
103 import android.view.MenuInflater;
104 import android.view.MenuItem;
105 import android.view.View;
106 import android.view.View.OnClickListener;
107 import android.view.ViewGroup;
108 import android.widget.AdapterView;
109 import android.widget.AdapterView.OnItemClickListener;
110 import android.widget.AdapterView.OnItemSelectedListener;
111 import android.widget.ArrayAdapter;
112 import android.widget.BaseAdapter;
113 import android.widget.Button;
114 import android.widget.FrameLayout;
115 import android.widget.ImageView;
116 import android.widget.LinearLayout;
117 import android.widget.ListView;
118 import android.widget.NumberPicker;
119 import android.widget.ProgressBar;
120 import android.widget.Spinner;
121 import android.widget.Switch;
122 import android.widget.TabHost;
123 import android.widget.TabHost.OnTabChangeListener;
124 import android.widget.TabHost.TabContentFactory;
125 import android.widget.TabHost.TabSpec;
126 import android.widget.TabWidget;
127 import android.widget.TextView;
128 import android.widget.Toast;
129
130 import com.android.internal.logging.MetricsLogger;
131 import com.android.internal.telephony.PhoneConstants;
132 import com.android.settings.DataUsageUtils;
133 import com.android.settings.drawable.InsetBoundsDrawable;
134 import com.android.settings.net.DataUsageMeteredSettings;
135 import com.android.settings.search.BaseSearchIndexProvider;
136 import com.android.settings.search.Indexable;
137 import com.android.settings.search.SearchIndexableRaw;
138 import com.android.settings.widget.ChartDataUsageView;
139 import com.android.settings.widget.ChartDataUsageView.DataUsageChartListener;
140 import com.android.settings.widget.ChartNetworkSeriesView;
141 import com.android.settingslib.AppItem;
142 import com.android.settingslib.NetworkPolicyEditor;
143 import com.android.settingslib.net.ChartData;
144 import com.android.settingslib.net.ChartDataLoader;
145 import com.android.settingslib.net.SummaryForAllUidLoader;
146 import com.android.settingslib.net.UidDetail;
147 import com.android.settingslib.net.UidDetailProvider;
148 import com.google.android.collect.Lists;
149
150 import libcore.util.Objects;
151
152 import java.util.ArrayList;
153 import java.util.Collections;
154 import java.util.HashMap;
155 import java.util.List;
156 import java.util.Locale;
157 import java.util.Map;
158 import java.util.Set;
159
160 /**
161  * Panel showing data usage history across various networks, including options
162  * to inspect based on usage cycle and control through {@link NetworkPolicy}.
163  */
164 public class DataUsageSummary extends HighlightingFragment implements Indexable {
165     private static final String TAG = "DataUsage";
166     private static final boolean LOGD = false;
167
168     // TODO: remove this testing code
169     private static final boolean TEST_ANIM = false;
170     private static final boolean TEST_RADIOS = false;
171
172     private static final String TEST_RADIOS_PROP = "test.radios";
173     private static final String TEST_SUBSCRIBER_PROP = "test.subscriberid";
174
175     private static final String TAB_3G = "3g";
176     private static final String TAB_4G = "4g";
177     private static final String TAB_MOBILE = "mobile";
178     private static final String TAB_WIFI = "wifi";
179     private static final String TAB_ETHERNET = "ethernet";
180
181     private static final String TAG_CONFIRM_DATA_DISABLE = "confirmDataDisable";
182     private static final String TAG_CONFIRM_DATA_RESET = "confirmDataReset";
183     private static final String TAG_CONFIRM_APP_RESTRICT_CELLULAR = "confirmAppRestrictCellular";
184     private static final String TAG_CONFIRM_LIMIT = "confirmLimit";
185     private static final String TAG_CYCLE_EDITOR = "cycleEditor";
186     private static final String TAG_WARNING_EDITOR = "warningEditor";
187     private static final String TAG_LIMIT_EDITOR = "limitEditor";
188     private static final String TAG_CONFIRM_RESTRICT = "confirmRestrict";
189     private static final String TAG_DENIED_RESTRICT = "deniedRestrict";
190     private static final String TAG_CONFIRM_APP_RESTRICT = "confirmAppRestrict";
191     private static final String TAG_APP_DETAILS = "appDetails";
192
193     private static final String DATA_USAGE_ENABLE_MOBILE_KEY = "data_usage_enable_mobile";
194     private static final String DATA_USAGE_DISABLE_MOBILE_LIMIT_KEY =
195             "data_usage_disable_mobile_limit";
196     private static final String DATA_USAGE_CYCLE_KEY = "data_usage_cycle";
197
198     public static final String EXTRA_SHOW_APP_IMMEDIATE_PKG = "showAppImmediatePkg";
199
200     private static final int LOADER_CHART_DATA = 2;
201     private static final int LOADER_SUMMARY = 3;
202     private static final int DATA_USAGE_BACKGROUND_FULL_ACCESS = 0;
203     private static final int DATA_USAGE_BACKGROUND_WLAN_ACCESS = 1;
204     private static final int DATA_USAGE_BACKGROUND_NO_ACCESS = 2;
205
206     private INetworkManagementService mNetworkService;
207     private INetworkStatsService mStatsService;
208     private NetworkPolicyManager mPolicyManager;
209     private TelephonyManager mTelephonyManager;
210     private SubscriptionManager mSubscriptionManager;
211
212     private INetworkStatsSession mStatsSession;
213
214     private static final String PREF_FILE = "data_usage";
215     private static final String PREF_SHOW_WIFI = "show_wifi";
216     private static final String PREF_SHOW_ETHERNET = "show_ethernet";
217     private static final String PREF_ENABLE_DATA_USAGE_NOTIFY = "enable_data_usage_notify";
218
219     private SharedPreferences mPrefs;
220
221     private TabHost mTabHost;
222     private ViewGroup mTabsContainer;
223     private TabWidget mTabWidget;
224     private ListView mListView;
225     private ChartNetworkSeriesView mSeries;
226     private ChartNetworkSeriesView mDetailedSeries;
227     private DataUsageAdapter mAdapter;
228
229     /** Distance to inset content from sides, when needed. */
230     private int mInsetSide = 0;
231
232     private ViewGroup mHeader;
233
234     private ViewGroup mNetworkSwitchesContainer;
235     private LinearLayout mNetworkSwitches;
236     private boolean mDataEnabledSupported;
237     private Switch mDataEnabled;
238     private View mDataEnabledView;
239     private boolean mDisableAtLimitSupported;
240     private Switch mDisableAtLimit;
241     private View mDisableAtLimitView;
242
243     private View mCycleView;
244     private Spinner mCycleSpinner;
245     private CycleAdapter mCycleAdapter;
246     private TextView mCycleSummary;
247
248     private ChartDataUsageView mChart;
249     private View mDisclaimer;
250     private TextView mEmpty;
251     private View mStupidPadding;
252
253     private View mAppDetail;
254     private ImageView mAppIcon;
255     private ViewGroup mAppTitles;
256     private TextView mAppTotal;
257     private TextView mAppForeground;
258     private TextView mAppBackground;
259     private Button mAppSettings;
260
261     private LinearLayout mAppSwitches;
262     private Switch mAppDataAlert;
263     private Switch mAppCellularAccess;
264     private View mAppRestrictView;
265     private View mAppDataAlertView;
266     private View mAppCellularAccessView;
267     private Spinner mRestrictSpinner;
268
269     private boolean mShowWifi = false;
270     private boolean mShowEthernet = false;
271     private boolean mShowAlerts = false;
272     private boolean mDataAlertsSupported = false;
273
274     private NetworkTemplate mTemplate;
275     private ChartData mChartData;
276
277     private AppItem mCurrentApp = null;
278
279     private Intent mAppSettingsIntent;
280
281     private NetworkPolicyEditor mPolicyEditor;
282
283     private String mCurrentTab = null;
284     private String mIntentTab = null;
285
286     private MenuItem mMenuRestrictBackground;
287     private MenuItem mMenuShowWifi;
288     private MenuItem mMenuShowEthernet;
289     private MenuItem mMenuSimCards;
290     private MenuItem mMenuCellularNetworks;
291     private MenuItem mMenuDataAlerts;
292     private MenuItem mMenuResetStats;
293
294     private List<SubscriptionInfo> mSubInfoList;
295     private Map<Integer,String> mMobileTagMap;
296
297     /** Flag used to ignore listeners during binding. */
298     private boolean mBinding;
299
300     private UidDetailProvider mUidDetailProvider;
301
302     // Indicates request to show app immediately rather than list.
303     private String mShowAppImmediatePkg;
304
305     /**
306      * Local cache of data enabled for subId, used to work around delays.
307      */
308     private final Map<String, Boolean> mMobileDataEnabled = new HashMap<String, Boolean>();
309
310     @Override
311     protected int getMetricsCategory() {
312         return MetricsLogger.DATA_USAGE_SUMMARY;
313     }
314
315     @Override
316     public void onCreate(Bundle savedInstanceState) {
317         super.onCreate(savedInstanceState);
318         final Context context = getActivity();
319
320         mNetworkService = INetworkManagementService.Stub.asInterface(
321                 ServiceManager.getService(Context.NETWORKMANAGEMENT_SERVICE));
322         mStatsService = INetworkStatsService.Stub.asInterface(
323                 ServiceManager.getService(Context.NETWORK_STATS_SERVICE));
324         mPolicyManager = NetworkPolicyManager.from(context);
325         mTelephonyManager = TelephonyManager.from(context);
326         mSubscriptionManager = SubscriptionManager.from(context);
327
328         mPrefs = getActivity().getSharedPreferences(PREF_FILE, Context.MODE_PRIVATE);
329
330         mPolicyEditor = new NetworkPolicyEditor(mPolicyManager);
331         mPolicyEditor.read();
332
333         mSubInfoList = mSubscriptionManager.getActiveSubscriptionInfoList();
334         mMobileTagMap = initMobileTabTag(mSubInfoList);
335
336         try {
337             if (!mNetworkService.isBandwidthControlEnabled()) {
338                 Log.w(TAG, "No bandwidth control; leaving");
339                 getActivity().finish();
340             }
341         } catch (RemoteException e) {
342             Log.w(TAG, "No bandwidth control; leaving");
343             getActivity().finish();
344         }
345
346         try {
347             mStatsSession = mStatsService.openSession();
348         } catch (RemoteException e) {
349             throw new RuntimeException(e);
350         }
351
352         mShowWifi = mPrefs.getBoolean(PREF_SHOW_WIFI, true);
353         mShowEthernet = mPrefs.getBoolean(PREF_SHOW_ETHERNET, false);
354         mShowAlerts = mPrefs.getBoolean(PREF_ENABLE_DATA_USAGE_NOTIFY, false);
355
356         // override preferences when no mobile radio
357         if (!hasReadyMobileRadio(context)) {
358             mShowWifi = true;
359             mShowEthernet = true;
360         }
361
362         mUidDetailProvider = new UidDetailProvider(context);
363
364         Bundle arguments = getArguments();
365         if (arguments != null) {
366             mShowAppImmediatePkg = arguments.getString(EXTRA_SHOW_APP_IMMEDIATE_PKG);
367         }
368
369         setHasOptionsMenu(true);
370     }
371
372     @Override
373     public View onCreateView(LayoutInflater inflater, ViewGroup container,
374             Bundle savedInstanceState) {
375
376         final Context context = inflater.getContext();
377         final View view = inflater.inflate(R.layout.data_usage_summary, container, false);
378
379
380         mTabHost = (TabHost) view.findViewById(android.R.id.tabhost);
381         mTabsContainer = (ViewGroup) view.findViewById(R.id.tabs_container);
382         mTabWidget = (TabWidget) view.findViewById(android.R.id.tabs);
383         mListView = (ListView) view.findViewById(android.R.id.list);
384
385         // decide if we need to manually inset our content, or if we should rely
386         // on parent container for inset.
387         final boolean shouldInset = mListView.getScrollBarStyle()
388                 == View.SCROLLBARS_OUTSIDE_OVERLAY;
389         mInsetSide = 0;
390
391         // adjust padding around tabwidget as needed
392         prepareCustomPreferencesList(container, view, mListView, false);
393
394         mTabHost.setup();
395         mTabHost.setOnTabChangedListener(mTabListener);
396
397         mHeader = (ViewGroup) inflater.inflate(R.layout.data_usage_header, mListView, false);
398         mHeader.setClickable(true);
399
400         mListView.addHeaderView(new View(context), null, true);
401         mListView.addHeaderView(mHeader, null, true);
402         mListView.setItemsCanFocus(true);
403
404         if (mInsetSide > 0) {
405             // inset selector and divider drawables
406             insetListViewDrawables(mListView, mInsetSide);
407             mHeader.setPaddingRelative(mInsetSide, 0, mInsetSide, 0);
408         }
409
410         {
411             // bind network switches
412             mNetworkSwitchesContainer = (ViewGroup) mHeader.findViewById(
413                     R.id.network_switches_container);
414             mNetworkSwitches = (LinearLayout) mHeader.findViewById(R.id.network_switches);
415
416             mDataEnabled = new Switch(inflater.getContext());
417             mDataEnabled.setClickable(false);
418             mDataEnabled.setFocusable(false);
419             mDataEnabledView = inflatePreference(inflater, mNetworkSwitches, mDataEnabled);
420             mDataEnabledView.setTag(R.id.preference_highlight_key,
421                     DATA_USAGE_ENABLE_MOBILE_KEY);
422             mDataEnabledView.setClickable(true);
423             mDataEnabledView.setFocusable(true);
424             mDataEnabledView.setOnClickListener(mDataEnabledListener);
425             mNetworkSwitches.addView(mDataEnabledView);
426
427             mDisableAtLimit = new Switch(inflater.getContext());
428             mDisableAtLimit.setClickable(false);
429             mDisableAtLimit.setFocusable(false);
430             mDisableAtLimitView = inflatePreference(inflater, mNetworkSwitches, mDisableAtLimit);
431             mDisableAtLimitView.setTag(R.id.preference_highlight_key,
432                     DATA_USAGE_DISABLE_MOBILE_LIMIT_KEY);
433             mDisableAtLimitView.setClickable(true);
434             mDisableAtLimitView.setFocusable(true);
435             mDisableAtLimitView.setOnClickListener(mDisableAtLimitListener);
436             mNetworkSwitches.addView(mDisableAtLimitView);
437
438             mCycleView = inflater.inflate(R.layout.data_usage_cycles, mNetworkSwitches, false);
439             mCycleView.setTag(R.id.preference_highlight_key, DATA_USAGE_CYCLE_KEY);
440             mCycleSpinner = (Spinner) mCycleView.findViewById(R.id.cycles_spinner);
441             mCycleAdapter = new CycleAdapter(context);
442             mCycleSpinner.setAdapter(mCycleAdapter);
443             mCycleSpinner.setOnItemSelectedListener(mCycleListener);
444             mCycleSummary = (TextView) mCycleView.findViewById(R.id.cycle_summary);
445             mNetworkSwitches.addView(mCycleView);
446             mSeries = (ChartNetworkSeriesView)view.findViewById(R.id.series);
447             mDetailedSeries = (ChartNetworkSeriesView)view.findViewById(R.id.detail_series);
448         }
449
450         mChart = (ChartDataUsageView) mHeader.findViewById(R.id.chart);
451         mChart.setListener(mChartListener);
452         mChart.bindNetworkPolicy(null);
453
454         {
455             // bind app detail controls
456             mAppDetail = mHeader.findViewById(R.id.app_detail);
457             mAppIcon = (ImageView) mAppDetail.findViewById(R.id.app_icon);
458             mAppTitles = (ViewGroup) mAppDetail.findViewById(R.id.app_titles);
459             mAppForeground = (TextView) mAppDetail.findViewById(R.id.app_foreground);
460             mAppBackground = (TextView) mAppDetail.findViewById(R.id.app_background);
461             mAppSwitches = (LinearLayout) mAppDetail.findViewById(R.id.app_switches);
462
463             mAppSettings = (Button) mAppDetail.findViewById(R.id.app_settings);
464
465             ArrayAdapter<String> restrictAdapter = new ArrayAdapter<String>(
466                     inflater.getContext(), android.R.layout.simple_spinner_dropdown_item,
467                     getResources().getStringArray(R.array.background_data_access_choices));
468
469             mRestrictSpinner = new Spinner(inflater.getContext());
470             mRestrictSpinner.setAdapter(restrictAdapter);
471             mRestrictSpinner.setOnItemSelectedListener(mAppRestrictListener);
472
473             mAppRestrictView = inflatePreferenceWithInvisibleWidget(inflater,
474                     mAppSwitches, mRestrictSpinner);
475             mAppRestrictView.setClickable(true);
476             mAppRestrictView.setFocusable(true);
477             mAppRestrictView.setOnClickListener(new View.OnClickListener() {
478                 @Override
479                 public void onClick(View v) {
480                     mRestrictSpinner.performClick();
481                 }
482             });
483             mAppSwitches.addView(mAppRestrictView);
484
485             //switch for per app data alert enable/disable
486             mAppDataAlert = new Switch(inflater.getContext());
487             mAppDataAlert.setClickable(false);
488             mAppDataAlert.setFocusable(false);
489             mAppDataAlertView = inflatePreference(inflater, mAppSwitches, mAppDataAlert);
490             mAppDataAlertView.setClickable(true);
491             mAppDataAlertView.setFocusable(true);
492             mAppDataAlertView.setOnClickListener(mAppDataAlertListner);
493             mAppSwitches.addView(mAppDataAlertView);
494
495             // check if content provider is installed. If not, hide data alert switch
496             mDataAlertsSupported = DataUsageUtils.isDbEnabled(context);
497             if (!mDataAlertsSupported) {
498                 mAppDataAlertView.setVisibility(View.GONE);
499             }
500
501             // switch for per app cellular access enabled/disable
502             mAppCellularAccess = new Switch(inflater.getContext());
503             mAppCellularAccess.setClickable(false);
504             mAppCellularAccess.setFocusable(false);
505             mAppCellularAccessView = inflatePreference(inflater, mAppSwitches, mAppCellularAccess);
506             mAppCellularAccessView.setClickable(true);
507             mAppCellularAccessView.setFocusable(true);
508             mAppCellularAccessView.setOnClickListener(mAppRestrictCellularListener);
509             mAppSwitches.addView(mAppCellularAccessView);
510         }
511
512         mDisclaimer = mHeader.findViewById(R.id.disclaimer);
513         mEmpty = (TextView) mHeader.findViewById(android.R.id.empty);
514         mStupidPadding = mHeader.findViewById(R.id.stupid_padding);
515
516         final UserManager um = (UserManager) context.getSystemService(Context.USER_SERVICE);
517         mAdapter = new DataUsageAdapter(um, mUidDetailProvider, mInsetSide);
518         mListView.setOnItemClickListener(mListListener);
519         mListView.setAdapter(mAdapter);
520
521         showRequestedAppIfNeeded(view);
522
523         return view;
524     }
525
526     private void showRequestedAppIfNeeded(View rootView) {
527         if (mShowAppImmediatePkg == null) {
528             return;
529         }
530         try {
531             int uid = getActivity().getPackageManager().getPackageUid(mShowAppImmediatePkg,
532                 UserHandle.myUserId());
533             AppItem app = new AppItem(uid);
534             app.addUid(uid);
535
536             final UidDetail detail = mUidDetailProvider.getUidDetail(app.key, true);
537             // When we are going straight to an app then we are coming from App Info and want
538             // a header at the top.
539             FrameLayout pinnedHeader = (FrameLayout) rootView.findViewById(R.id.pinned_header);
540             AppHeader.createAppHeader(getActivity(), detail.icon, detail.label, null, pinnedHeader);
541             AppDetailsFragment.show(DataUsageSummary.this, app, detail.label, false);
542         } catch (NameNotFoundException e) {
543             Log.w(TAG, "Could not find " + mShowAppImmediatePkg, e);
544             Toast.makeText(getActivity(), getString(R.string.unknown_app), Toast.LENGTH_LONG)
545                     .show();
546             getActivity().finish();
547         }
548     }
549
550     @Override
551     public void onViewStateRestored(Bundle savedInstanceState) {
552         super.onViewStateRestored(savedInstanceState);
553
554         // pick default tab based on incoming intent
555         final Intent intent = getActivity().getIntent();
556         mIntentTab = computeTabFromIntent(intent);
557
558         // this kicks off chain reaction which creates tabs, binds the body to
559         // selected network, and binds chart, cycles and detail list.
560         updateTabs();
561     }
562
563     @Override
564     public void onResume() {
565         super.onResume();
566
567         getView().post(new Runnable() {
568             @Override
569             public void run() {
570                 highlightViewIfNeeded();
571             }
572         });
573
574         // kick off background task to update stats
575         new AsyncTask<Void, Void, Void>() {
576             @Override
577             protected Void doInBackground(Void... params) {
578                 try {
579                     // wait a few seconds before kicking off
580                     Thread.sleep(2 * DateUtils.SECOND_IN_MILLIS);
581                     mStatsService.forceUpdate();
582                 } catch (InterruptedException e) {
583                 } catch (RemoteException e) {
584                 }
585                 return null;
586             }
587
588             @Override
589             protected void onPostExecute(Void result) {
590                 if (isAdded()) {
591                     updateBody();
592                 }
593             }
594         }.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
595     }
596
597     @Override
598     public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
599         inflater.inflate(R.menu.data_usage, menu);
600     }
601
602     @Override
603     public void onPrepareOptionsMenu(Menu menu) {
604         final Context context = getActivity();
605         final boolean appDetailMode = isAppDetailMode();
606         final boolean isOwner = ActivityManager.getCurrentUser() == UserHandle.USER_OWNER;
607
608         mMenuShowWifi = menu.findItem(R.id.data_usage_menu_show_wifi);
609         if (hasWifiRadio(context) && hasReadyMobileRadio(context)) {
610             mMenuShowWifi.setVisible(!appDetailMode);
611         } else {
612             mMenuShowWifi.setVisible(false);
613         }
614
615         mMenuShowEthernet = menu.findItem(R.id.data_usage_menu_show_ethernet);
616         if (hasEthernet(context) && hasReadyMobileRadio(context)) {
617             mMenuShowEthernet.setVisible(!appDetailMode);
618         } else {
619             mMenuShowEthernet.setVisible(false);
620         }
621
622         mMenuRestrictBackground = menu.findItem(R.id.data_usage_menu_restrict_background);
623         mMenuRestrictBackground.setVisible(
624                 hasReadyMobileRadio(context) && isOwner && !appDetailMode);
625
626         final MenuItem metered = menu.findItem(R.id.data_usage_menu_metered);
627         if (hasReadyMobileRadio(context) || hasWifiRadio(context)) {
628             metered.setVisible(!appDetailMode);
629         } else {
630             metered.setVisible(false);
631         }
632
633         // TODO: show when multiple sims available
634         mMenuSimCards = menu.findItem(R.id.data_usage_menu_sim_cards);
635         mMenuSimCards.setVisible(false);
636
637         mMenuCellularNetworks = menu.findItem(R.id.data_usage_menu_cellular_networks);
638         mMenuCellularNetworks.setVisible(hasReadyMobileRadio(context)
639                 && !appDetailMode && isOwner);
640
641         final MenuItem help = menu.findItem(R.id.data_usage_menu_help);
642         String helpUrl;
643         if (!TextUtils.isEmpty(helpUrl = getResources().getString(R.string.help_url_data_usage))) {
644             HelpUtils.prepareHelpMenuItem(getActivity(), help, helpUrl, getClass().getName());
645         } else {
646             help.setVisible(false);
647         }
648
649         mMenuDataAlerts = menu.findItem(R.id.data_usage_menu_data_alerts);
650
651         if (mDataAlertsSupported) {
652             mMenuDataAlerts.setVisible(!appDetailMode);
653         } else {
654             mMenuDataAlerts.setVisible(false);
655         }
656
657         mMenuResetStats = menu.findItem(R.id.data_usage_menu_reset_stats);
658         mMenuResetStats.setVisible(!appDetailMode);
659
660         updateMenuTitles();
661     }
662
663     private void updateMenuTitles() {
664         if (mPolicyManager.getRestrictBackground()) {
665             mMenuRestrictBackground.setTitle(R.string.data_usage_menu_allow_background);
666         } else {
667             mMenuRestrictBackground.setTitle(R.string.data_usage_menu_restrict_background);
668         }
669
670         if (mShowWifi) {
671             mMenuShowWifi.setTitle(R.string.data_usage_menu_hide_wifi);
672         } else {
673             mMenuShowWifi.setTitle(R.string.data_usage_menu_show_wifi);
674         }
675
676         if (mShowEthernet) {
677             mMenuShowEthernet.setTitle(R.string.data_usage_menu_hide_ethernet);
678         } else {
679             mMenuShowEthernet.setTitle(R.string.data_usage_menu_show_ethernet);
680         }
681         if (mShowAlerts) {
682             mMenuDataAlerts.setTitle(R.string.data_usage_menu_disable_data_alerts);
683         } else {
684             mMenuDataAlerts.setTitle(R.string.data_usage_menu_enable_data_alerts);
685         }
686     }
687
688     @Override
689     public boolean onOptionsItemSelected(MenuItem item) {
690         switch (item.getItemId()) {
691             case R.id.data_usage_menu_restrict_background: {
692                 final boolean restrictBackground = !mPolicyManager.getRestrictBackground();
693                 if (restrictBackground) {
694                     ConfirmRestrictFragment.show(this);
695                 } else {
696                     // no confirmation to drop restriction
697                     setRestrictBackground(false);
698                 }
699                 return true;
700             }
701             case R.id.data_usage_menu_show_wifi: {
702                 mShowWifi = !mShowWifi;
703                 mPrefs.edit().putBoolean(PREF_SHOW_WIFI, mShowWifi).apply();
704                 updateMenuTitles();
705                 updateTabs();
706                 return true;
707             }
708             case R.id.data_usage_menu_show_ethernet: {
709                 mShowEthernet = !mShowEthernet;
710                 mPrefs.edit().putBoolean(PREF_SHOW_ETHERNET, mShowEthernet).apply();
711                 updateMenuTitles();
712                 updateTabs();
713                 return true;
714             }
715             case R.id.data_usage_menu_sim_cards: {
716                 // TODO: hook up to sim cards
717                 return true;
718             }
719             case R.id.data_usage_menu_cellular_networks: {
720                 final Intent intent = new Intent(Intent.ACTION_MAIN);
721                 intent.setComponent(new ComponentName("com.android.phone",
722                         "com.android.phone.MobileNetworkSettings"));
723                 startActivity(intent);
724                 return true;
725             }
726             case R.id.data_usage_menu_metered: {
727                 final SettingsActivity sa = (SettingsActivity) getActivity();
728                 sa.startPreferencePanel(DataUsageMeteredSettings.class.getCanonicalName(), null,
729                         R.string.data_usage_metered_title, null, this, 0);
730                 return true;
731             }
732             case R.id.data_usage_menu_reset_stats: {
733                 ConfirmDataResetFragment.show(DataUsageSummary.this, mTemplate);
734                 return true;
735             }
736             case R.id.data_usage_menu_data_alerts: {
737                 updateShowAlertsState(!mShowAlerts);
738                 return true;
739             }
740         }
741         return false;
742     }
743
744     @Override
745     public void onDestroy() {
746         mDataEnabledView = null;
747         mDisableAtLimitView = null;
748
749         mUidDetailProvider.clearCache();
750         mUidDetailProvider = null;
751
752         TrafficStats.closeQuietly(mStatsSession);
753
754         super.onDestroy();
755     }
756
757     private void updateShowAlertsState(boolean showAlert) {
758         mShowAlerts = showAlert;
759         mPrefs.edit().putBoolean(PREF_ENABLE_DATA_USAGE_NOTIFY, mShowAlerts).apply();
760         updateMenuTitles();
761         DataUsageUtils.enableDataUsageService(getContext(), mShowAlerts);
762     }
763
764     /**
765      * Build and assign {@link LayoutTransition} to various containers. Should
766      * only be assigned after initial layout is complete.
767      */
768     private void ensureLayoutTransitions() {
769         if (mShowAppImmediatePkg != null) {
770             // If we are skipping right to showing an app, we don't care about transitions.
771             return;
772         }
773         // skip when already setup
774         if (mChart.getLayoutTransition() != null) return;
775
776         mTabsContainer.setLayoutTransition(buildLayoutTransition());
777         mHeader.setLayoutTransition(buildLayoutTransition());
778         mNetworkSwitchesContainer.setLayoutTransition(buildLayoutTransition());
779
780         final LayoutTransition chartTransition = buildLayoutTransition();
781         chartTransition.disableTransitionType(LayoutTransition.APPEARING);
782         chartTransition.disableTransitionType(LayoutTransition.DISAPPEARING);
783         mChart.setLayoutTransition(chartTransition);
784     }
785
786     private static LayoutTransition buildLayoutTransition() {
787         final LayoutTransition transition = new LayoutTransition();
788         if (TEST_ANIM) {
789             transition.setDuration(1500);
790         }
791         transition.setAnimateParentHierarchy(false);
792         return transition;
793     }
794
795     /**
796      * Rebuild all tabs based on {@link NetworkPolicyEditor} and
797      * {@link #mShowWifi}, hiding the tabs entirely when applicable. Selects
798      * first tab, and kicks off a full rebind of body contents.
799      */
800     private void updateTabs() {
801         final Context context = getActivity();
802         mTabHost.clearAllTabs();
803
804         int simCount = mTelephonyManager.getSimCount();
805
806         List<SubscriptionInfo> sirs = mSubscriptionManager.getActiveSubscriptionInfoList();
807         if (sirs != null) {
808             for (SubscriptionInfo sir : sirs) {
809                 addMobileTab(context, sir, (simCount > 1));
810             }
811         }
812
813         if (mShowWifi && hasWifiRadio(context)) {
814             mTabHost.addTab(buildTabSpec(TAB_WIFI, R.string.data_usage_tab_wifi));
815         }
816
817         if (mShowEthernet && hasEthernet(context)) {
818             mTabHost.addTab(buildTabSpec(TAB_ETHERNET, R.string.data_usage_tab_ethernet));
819         }
820
821         if (getResources().getBoolean(R.bool.config_gcf_disable_default_tabtext_allcaps)) {
822             for (int i = 0; i < mTabWidget.getTabCount(); i++) {
823                 TextView tv = (TextView) mTabWidget.getChildAt(i).findViewById(android.R.id.title);
824                 tv.setAllCaps(false);
825             }
826         }
827
828         final boolean noTabs = mTabWidget.getTabCount() == 0;
829         final boolean multipleTabs = mTabWidget.getTabCount() > 1;
830         mTabWidget.setVisibility(multipleTabs ? View.VISIBLE : View.GONE);
831         if (mIntentTab != null) {
832             if (Objects.equal(mIntentTab, mTabHost.getCurrentTabTag())) {
833                 // already hit updateBody() when added; ignore
834                 updateBody();
835             } else {
836                 mTabHost.setCurrentTabByTag(mIntentTab);
837             }
838             mIntentTab = null;
839         } else if (noTabs) {
840             // no usable tabs, so hide body
841             updateBody();
842         } else {
843             // already hit updateBody() when added; ignore
844         }
845     }
846
847     /**
848      * Factory that provide empty {@link View} to make {@link TabHost} happy.
849      */
850     private TabContentFactory mEmptyTabContent = new TabContentFactory() {
851         @Override
852         public View createTabContent(String tag) {
853             return new View(mTabHost.getContext());
854         }
855     };
856
857     /**
858      * Build {@link TabSpec} with thin indicator, and empty content.
859      */
860     private TabSpec buildTabSpec(String tag, int titleRes) {
861         return mTabHost.newTabSpec(tag).setIndicator(getText(titleRes)).setContent(
862                 mEmptyTabContent);
863     }
864
865     /**
866      * Build {@link TabSpec} with thin indicator, and empty content.
867      */
868     private TabSpec buildTabSpec(String tag, CharSequence title) {
869         return mTabHost.newTabSpec(tag).setIndicator(title).setContent(
870                 mEmptyTabContent);
871     }
872
873
874     private OnTabChangeListener mTabListener = new OnTabChangeListener() {
875         @Override
876         public void onTabChanged(String tabId) {
877             // user changed tab; update body
878             updateBody();
879         }
880     };
881
882     /**
883      * Update body content based on current tab. Loads
884      * {@link NetworkStatsHistory} and {@link NetworkPolicy} from system, and
885      * binds them to visible controls.
886      */
887     private void updateBody() {
888         mBinding = true;
889         if (!isAdded()) return;
890
891         final Context context = getActivity();
892         final Resources resources = context.getResources();
893         final String currentTab = mTabHost.getCurrentTabTag();
894         final boolean isOwner = ActivityManager.getCurrentUser() == UserHandle.USER_OWNER;
895
896         if (currentTab == null) {
897             Log.w(TAG, "no tab selected; hiding body");
898             mListView.setVisibility(View.GONE);
899             return;
900         } else {
901             mListView.setVisibility(View.VISIBLE);
902         }
903
904         mCurrentTab = currentTab;
905
906         if (LOGD) Log.d(TAG, "updateBody() with currentTab=" + currentTab);
907
908         mDataEnabledSupported = isOwner;
909         mDisableAtLimitSupported = true;
910
911         // TODO: remove mobile tabs when SIM isn't ready probably by
912         // TODO: using SubscriptionManager.getActiveSubscriptionInfoList.
913         if (LOGD) Log.d(TAG, "updateBody() isMobileTab=" + isMobileTab(currentTab));
914
915         if (isMobileTab(currentTab)) {
916             if (LOGD) Log.d(TAG, "updateBody() mobile tab");
917             setPreferenceTitle(mDataEnabledView, R.string.data_usage_enable_mobile);
918             setPreferenceTitle(mDisableAtLimitView, R.string.data_usage_disable_mobile_limit);
919             mDataEnabledSupported = isMobileDataAvailable(getSubId(currentTab));
920
921             // Match mobile traffic for this subscriber, but normalize it to
922             // catch any other merged subscribers.
923             mTemplate = buildTemplateMobileAll(
924                     getActiveSubscriberId(context, getSubId(currentTab)));
925             mTemplate = NetworkTemplate.normalize(mTemplate,
926                     mTelephonyManager.getMergedSubscriberIds());
927
928         } else if (TAB_3G.equals(currentTab)) {
929             if (LOGD) Log.d(TAG, "updateBody() 3g tab");
930             setPreferenceTitle(mDataEnabledView, R.string.data_usage_enable_3g);
931             setPreferenceTitle(mDisableAtLimitView, R.string.data_usage_disable_3g_limit);
932             // TODO: bind mDataEnabled to 3G radio state
933             mTemplate = buildTemplateMobile3gLower(getActiveSubscriberId(context));
934
935         } else if (TAB_4G.equals(currentTab)) {
936             if (LOGD) Log.d(TAG, "updateBody() 4g tab");
937             setPreferenceTitle(mDataEnabledView, R.string.data_usage_enable_4g);
938             setPreferenceTitle(mDisableAtLimitView, R.string.data_usage_disable_4g_limit);
939             // TODO: bind mDataEnabled to 4G radio state
940             mTemplate = buildTemplateMobile4g(getActiveSubscriberId(context));
941
942         } else if (TAB_WIFI.equals(currentTab)) {
943             // wifi doesn't have any controls
944             if (LOGD) Log.d(TAG, "updateBody() wifi tab");
945             mDataEnabledSupported = false;
946             mDisableAtLimitSupported = false;
947             mTemplate = buildTemplateWifiWildcard();
948
949         } else if (TAB_ETHERNET.equals(currentTab)) {
950             // ethernet doesn't have any controls
951             if (LOGD) Log.d(TAG, "updateBody() ethernet tab");
952             mDataEnabledSupported = false;
953             mDisableAtLimitSupported = false;
954             mTemplate = buildTemplateEthernet();
955
956         } else {
957             if (LOGD) Log.d(TAG, "updateBody() unknown tab");
958             throw new IllegalStateException("unknown tab: " + currentTab);
959         }
960
961         mPolicyEditor.read();
962         final NetworkPolicy policy = mPolicyEditor.getPolicy(mTemplate);
963         if (policy != null) {
964             final long currentTime = System.currentTimeMillis();
965             final long start = computeLastCycleBoundary(currentTime, policy);
966             final long end = currentTime;
967             long totalBytes = 0;
968
969             try {
970                 totalBytes = mStatsService.getNetworkTotalBytes(policy.template, start, end);
971             } catch (RuntimeException e) {
972             } catch (RemoteException e) {
973             }
974
975             if (policy.isOverLimit(totalBytes) && policy.lastLimitSnooze < start) {
976                 setPreferenceSummary(mDataEnabledView,
977                         getString(R.string.data_usage_cellular_data_summary));
978             } else {
979                 final TextView summary = (TextView) mDataEnabledView
980                         .findViewById(android.R.id.summary);
981                 summary.setVisibility(View.GONE);
982             }
983         }
984
985         // kick off loader for network history
986         // TODO: consider chaining two loaders together instead of reloading
987         // network history when showing app detail.
988         getLoaderManager().restartLoader(LOADER_CHART_DATA,
989                 ChartDataLoader.buildArgs(mTemplate, mCurrentApp), mChartDataCallbacks);
990
991         // detail mode can change visible menus, invalidate
992         getActivity().invalidateOptionsMenu();
993
994         mBinding = false;
995
996         int seriesColor = context.getColor(R.color.sim_noitification);
997         if (mCurrentTab != null && mCurrentTab.startsWith(TAB_MOBILE)) {
998             final int slotId = Integer.parseInt(mCurrentTab.substring(TAB_MOBILE.length(),
999                     mCurrentTab.length()));
1000             final SubscriptionInfo sir = mSubscriptionManager
1001                     .getActiveSubscriptionInfoForSimSlotIndex(slotId);
1002
1003             if (sir != null) {
1004                 seriesColor = sir.getIconTint();
1005             }
1006         }
1007
1008         final int secondaryColor = Color.argb(127, Color.red(seriesColor), Color.green(seriesColor),
1009                 Color.blue(seriesColor));
1010         mSeries.setChartColor(Color.BLACK, seriesColor, secondaryColor);
1011         mDetailedSeries.setChartColor(Color.BLACK, seriesColor, secondaryColor);
1012     }
1013
1014     private boolean isAppDetailMode() {
1015         return mCurrentApp != null;
1016     }
1017
1018     /**
1019      * Update UID details panels to match {@link #mCurrentApp}, showing or
1020      * hiding them depending on {@link #isAppDetailMode()}.
1021      */
1022     private void updateAppDetail() {
1023         final Context context = getActivity();
1024         final PackageManager pm = context.getPackageManager();
1025         final LayoutInflater inflater = getActivity().getLayoutInflater();
1026
1027         if (isAppDetailMode()) {
1028             mAppDetail.setVisibility(View.VISIBLE);
1029             mCycleAdapter.setChangeVisible(false);
1030         } else {
1031             mAppDetail.setVisibility(View.GONE);
1032             mCycleAdapter.setChangeVisible(true);
1033
1034             // hide detail stats when not in detail mode
1035             mChart.bindDetailNetworkStats(null);
1036             return;
1037         }
1038
1039         // remove warning/limit sweeps while in detail mode
1040         mChart.bindNetworkPolicy(null);
1041
1042         // show icon and all labels appearing under this app
1043         final int uid = mCurrentApp.key;
1044         final UidDetail detail = mUidDetailProvider.getUidDetail(uid, true);
1045         mAppIcon.setImageDrawable(detail.icon);
1046
1047         mAppTitles.removeAllViews();
1048
1049         View title = null;
1050         if (detail.detailLabels != null) {
1051             final int n = detail.detailLabels.length;
1052             for (int i = 0; i < n; ++i) {
1053                 CharSequence label = detail.detailLabels[i];
1054                 CharSequence contentDescription = detail.detailContentDescriptions[i];
1055                 title = inflater.inflate(R.layout.data_usage_app_title, mAppTitles, false);
1056                 TextView appTitle = (TextView) title.findViewById(R.id.app_title);
1057                 appTitle.setText(label);
1058                 appTitle.setContentDescription(contentDescription);
1059                 mAppTitles.addView(title);
1060             }
1061         } else {
1062             title = inflater.inflate(R.layout.data_usage_app_title, mAppTitles, false);
1063             TextView appTitle = (TextView) title.findViewById(R.id.app_title);
1064             appTitle.setText(detail.label);
1065             appTitle.setContentDescription(detail.contentDescription);
1066             mAppTitles.addView(title);
1067         }
1068
1069         // Remember last slot for summary
1070         if (title != null) {
1071             mAppTotal = (TextView) title.findViewById(R.id.app_summary);
1072         } else {
1073             mAppTotal = null;
1074         }
1075
1076         // enable settings button when package provides it
1077         final String[] packageNames = pm.getPackagesForUid(uid);
1078         if (packageNames != null && packageNames.length > 0) {
1079             mAppSettingsIntent = new Intent(Intent.ACTION_MANAGE_NETWORK_USAGE);
1080             mAppSettingsIntent.addCategory(Intent.CATEGORY_DEFAULT);
1081
1082             // Search for match across all packages
1083             boolean matchFound = false;
1084             for (String packageName : packageNames) {
1085                 mAppSettingsIntent.setPackage(packageName);
1086                 if (pm.resolveActivity(mAppSettingsIntent, 0) != null) {
1087                     matchFound = true;
1088                     break;
1089                 }
1090             }
1091
1092             mAppSettings.setOnClickListener(new OnClickListener() {
1093                 @Override
1094                 public void onClick(View v) {
1095                     if (!isAdded()) {
1096                         return;
1097                     }
1098
1099                     // TODO: target towards entire UID instead of just first package
1100                     getActivity().startActivityAsUser(mAppSettingsIntent,
1101                             new UserHandle(UserHandle.getUserId(uid)));
1102                 }
1103             });
1104             mAppSettings.setEnabled(matchFound);
1105             mAppSettings.setVisibility(View.VISIBLE);
1106
1107         } else {
1108             mAppSettingsIntent = null;
1109             mAppSettings.setOnClickListener(null);
1110             mAppSettings.setVisibility(View.GONE);
1111         }
1112
1113         updateDetailData();
1114
1115         if (UserHandle.isApp(uid) && isBandwidthControlEnabled()) {
1116             setPreferenceTitle(mAppRestrictView, R.string.background_data_access);
1117
1118             int backgroundPolicy = getAppRestrictBackground();
1119             final int summaryResId, position;
1120             if ((backgroundPolicy & POLICY_REJECT_METERED_BACKGROUND) != 0) {
1121                 if ((backgroundPolicy & POLICY_REJECT_ON_WLAN_BACKGROUND) != 0) {
1122                     summaryResId = R.string.allow_background_none;
1123                     position = DATA_USAGE_BACKGROUND_NO_ACCESS;
1124                 } else {
1125                     summaryResId = R.string.allow_background_wlan;
1126                     position = DATA_USAGE_BACKGROUND_WLAN_ACCESS;
1127                 }
1128             } else {
1129                 summaryResId = R.string.allow_background_both;
1130                 position = DATA_USAGE_BACKGROUND_FULL_ACCESS;
1131             }
1132             setPreferenceSummary(mAppRestrictView, getString(summaryResId));
1133             mRestrictSpinner.setSelection(position);
1134             mAppRestrictView.setVisibility(View.VISIBLE);
1135
1136             if (isMobileTab(mCurrentTab) || TAB_3G.equals(mCurrentTab)
1137                     || TAB_4G.equals(mCurrentTab)) {
1138                 setPreferenceTitle(mAppCellularAccessView, R.string.restrict_cellular_access_title);
1139                 setPreferenceSummary(mAppCellularAccessView,
1140                         getString(R.string.restrict_cellular_access_summary));
1141                 mAppCellularAccessView.setVisibility(View.VISIBLE);
1142                 mAppCellularAccess.setChecked(getAppRestrictCellular());
1143             } else {
1144                 mAppCellularAccessView.setVisibility(View.GONE);
1145             }
1146
1147             if (mDataAlertsSupported) {
1148                 setPreferenceTitle(mAppDataAlertView, R.string.mobile_data_alert);
1149                 setPreferenceSummary(mAppDataAlertView,
1150                         getString(R.string.mobile_data_alert_summary));
1151
1152                 mAppDataAlertView.setVisibility(View.VISIBLE);
1153                 mAppDataAlert.setChecked(getAppDataAlert());
1154             } else {
1155                 mAppDataAlertView.setVisibility(View.GONE);
1156             }
1157         } else {
1158             mAppRestrictView.setVisibility(View.GONE);
1159             mAppDataAlertView.setVisibility(View.GONE);
1160             mAppCellularAccessView.setVisibility(View.GONE);
1161         }
1162     }
1163
1164     private void setPolicyWarningBytes(long warningBytes) {
1165         if (LOGD) Log.d(TAG, "setPolicyWarningBytes()");
1166         mPolicyEditor.setPolicyWarningBytes(mTemplate, warningBytes);
1167         updatePolicy(false);
1168     }
1169
1170     private void setPolicyLimitBytes(long limitBytes) {
1171         if (LOGD) Log.d(TAG, "setPolicyLimitBytes()");
1172         mPolicyEditor.setPolicyLimitBytes(mTemplate, limitBytes);
1173         updatePolicy(false);
1174     }
1175
1176     private boolean isMobileDataEnabled(int subId) {
1177         if (LOGD) Log.d(TAG, "isMobileDataEnabled:+ subId=" + subId);
1178         boolean isEnable = false;
1179         if (mMobileDataEnabled.get(String.valueOf(subId)) != null) {
1180             //TODO: deprecate and remove this once enabled flag is on policy
1181             //Multiple Subscriptions, the value need to be reseted
1182             isEnable = mMobileDataEnabled.get(String.valueOf(subId)).booleanValue();
1183             if (LOGD) {
1184                 Log.d(TAG, "isMobileDataEnabled: != null, subId=" + subId
1185                         + " isEnable=" + isEnable);
1186             }
1187             mMobileDataEnabled.put(String.valueOf(subId), null);
1188         } else {
1189             // SUB SELECT
1190             isEnable = mTelephonyManager.getDataEnabled(subId);
1191             if (LOGD) {
1192                 Log.d(TAG, "isMobileDataEnabled: == null, subId=" + subId
1193                         + " isEnable=" + isEnable);
1194             }
1195         }
1196         return isEnable;
1197     }
1198
1199     private void setMobileDataEnabled(int subId, boolean enabled) {
1200         if (LOGD) Log.d(TAG, "setMobileDataEnabled: subId = " + subId + " enabled = " + enabled);
1201         int dataSubId = mSubscriptionManager.getDefaultDataSubId();
1202         mTelephonyManager.setDataEnabled(subId, enabled);
1203         mMobileDataEnabled.put(String.valueOf(subId), enabled);
1204         updatePolicy(false);
1205     }
1206
1207     private void resetDataStats(NetworkTemplate template) {
1208         // kick off background task to reset stats
1209         new AsyncTask<Void, Void, Void>() {
1210             @Override
1211             protected Void doInBackground(Void... params) {
1212                 try {
1213                     mStatsService.resetDataUsageHistoryForAllUid(mTemplate);
1214                     mPolicyEditor.setPolicyLimitBytes(mTemplate,
1215                             mPolicyEditor.getPolicyLimitBytes(mTemplate));
1216                     mStatsService.forceUpdate();
1217                 } catch (RemoteException e) {
1218
1219                 }
1220                 return null;
1221             }
1222             @Override
1223             protected void onPostExecute (Void result) {
1224                 updateBody();
1225             }
1226
1227         }.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
1228     }
1229
1230     private boolean isNetworkPolicyModifiable(NetworkPolicy policy) {
1231         return policy != null && isBandwidthControlEnabled() && mDataEnabled.isChecked()
1232                 && ActivityManager.getCurrentUser() == UserHandle.USER_OWNER;
1233     }
1234
1235     private boolean isBandwidthControlEnabled() {
1236         try {
1237             return mNetworkService.isBandwidthControlEnabled();
1238         } catch (RemoteException e) {
1239             Log.w(TAG, "problem talking with INetworkManagementService: " + e);
1240             return false;
1241         }
1242     }
1243
1244     public void setRestrictBackground(boolean restrictBackground) {
1245         mPolicyManager.setRestrictBackground(restrictBackground);
1246         updateMenuTitles();
1247     }
1248
1249     private int getAppRestrictBackground() {
1250         final int uid = mCurrentApp.key;
1251         final int uidPolicy = mPolicyManager.getUidPolicy(uid);
1252
1253         return ((uidPolicy & POLICY_REJECT_METERED_BACKGROUND) | (uidPolicy &
1254                 POLICY_REJECT_ON_WLAN_BACKGROUND));
1255     }
1256
1257     private void setAppRestrictBackground(int newPolicy) {
1258         if (LOGD) Log.d(TAG, "setAppRestrictBackground()");
1259         final int uid = mCurrentApp.key;
1260         final int currentPolicy = mPolicyManager.getUidPolicy(uid);
1261
1262         if (newPolicy == currentPolicy) {
1263             return;
1264         }
1265
1266         if (((newPolicy & POLICY_REJECT_METERED_BACKGROUND) ^ (currentPolicy &
1267                 POLICY_REJECT_METERED_BACKGROUND)) != 0 ) {
1268             if ((newPolicy & POLICY_REJECT_METERED_BACKGROUND) != 0) {
1269                 mPolicyManager.addUidPolicy(uid, POLICY_REJECT_METERED_BACKGROUND);
1270             } else {
1271                 mPolicyManager.removeUidPolicy(uid, POLICY_REJECT_METERED_BACKGROUND);
1272             }
1273         }
1274
1275         if (((newPolicy & POLICY_REJECT_ON_WLAN_BACKGROUND) ^ (currentPolicy &
1276                 POLICY_REJECT_ON_WLAN_BACKGROUND)) != 0 ) {
1277             if ((newPolicy & POLICY_REJECT_ON_WLAN_BACKGROUND) != 0) {
1278                 mPolicyManager.addUidPolicy(uid, POLICY_REJECT_ON_WLAN_BACKGROUND);
1279             } else {
1280                 mPolicyManager.removeUidPolicy(uid, POLICY_REJECT_ON_WLAN_BACKGROUND);
1281             }
1282         }
1283
1284         final int summaryResId;
1285         if ((newPolicy & POLICY_REJECT_METERED_BACKGROUND) != 0) {
1286             if ((newPolicy & POLICY_REJECT_ON_WLAN_BACKGROUND) != 0) {
1287                 summaryResId = R.string.allow_background_none;
1288             } else {
1289                 summaryResId = R.string.allow_background_wlan;
1290             }
1291         } else {
1292             summaryResId = R.string.allow_background_both;
1293         }
1294         setPreferenceSummary(mAppRestrictView, getString(summaryResId));
1295     }
1296
1297     private boolean getAppRestrictCellular() {
1298         final int uid = mCurrentApp.key;
1299         final int uidPolicy = mPolicyManager.getUidPolicy(uid);
1300         return (uidPolicy & POLICY_REJECT_ON_DATA) != 0;
1301     }
1302
1303     private void setAppRestrictCellular(boolean restrictCellular) {
1304         if (LOGD) Log.d(TAG, "setAppRestrictCellular()");
1305         final int uid = mCurrentApp.key;
1306         if (restrictCellular) {
1307             mPolicyManager.addUidPolicy(uid, POLICY_REJECT_ON_DATA);
1308         } else {
1309             mPolicyManager.removeUidPolicy(uid, POLICY_REJECT_ON_DATA);
1310         }
1311         mAppCellularAccess.setChecked(restrictCellular);
1312     }
1313
1314
1315     private void setAppDataAlert(boolean enableDataAlert) {
1316         final int uid = mCurrentApp.key;
1317
1318         // Get app's details to send to the DataUsage Provider. Don't block if not in the
1319         // DetailProvider cache. (should be in the cache, as the app's label had already
1320         // been displayed in the list of apps)
1321         UidDetail detail = mUidDetailProvider.getUidDetail(uid, false);
1322         String label = detail != null ? detail.label.toString() : "";
1323
1324         try {
1325             DataUsageUtils.enableApp(getContext(), uid, enableDataAlert, label);
1326         } catch (Exception e) {
1327             //content provider may not be installed.
1328             Log.d(TAG, "Unable to set data alert state");
1329             return;
1330         }
1331         mAppDataAlert.setChecked(enableDataAlert);
1332
1333         // automatically enable global alert for notifications when enabling first per app alert
1334         if (enableDataAlert && !mShowAlerts) {
1335             updateShowAlertsState(true);
1336         }
1337     }
1338
1339     private boolean getAppDataAlert() {
1340         final int uid = mCurrentApp.key;
1341
1342         try {
1343             return DataUsageUtils.isAppEnabled(getContext(), uid);
1344         } catch (Exception e) {
1345             //content provider may not be installed.
1346             Log.d(TAG, "Unable to get data alert state");
1347             return false;
1348         }
1349     }
1350
1351     /**
1352      * Update chart sweeps and cycle list to reflect {@link NetworkPolicy} for
1353      * current {@link #mTemplate}.
1354      */
1355     private void updatePolicy(boolean refreshCycle) {
1356         boolean dataEnabledVisible = mDataEnabledSupported;
1357         boolean disableAtLimitVisible = mDisableAtLimitSupported;
1358
1359         if (isAppDetailMode()) {
1360             dataEnabledVisible = false;
1361             disableAtLimitVisible = false;
1362         }
1363
1364         // TODO: move enabled state directly into policy
1365         if (isMobileTab(mCurrentTab)) {
1366             mBinding = true;
1367             mDataEnabled.setChecked(isMobileDataEnabled(getSubId(mCurrentTab)));
1368             mBinding = false;
1369         }
1370
1371         final NetworkPolicy policy = mPolicyEditor.getPolicy(mTemplate);
1372         //SUB SELECT
1373         if (isNetworkPolicyModifiable(policy) && isMobileDataAvailable(getSubId(mCurrentTab))) {
1374             mDisableAtLimit.setChecked(policy != null && policy.limitBytes != LIMIT_DISABLED);
1375             if (!isAppDetailMode()) {
1376                 mChart.bindNetworkPolicy(policy);
1377             }
1378
1379         } else {
1380             // controls are disabled; don't bind warning/limit sweeps
1381             disableAtLimitVisible = false;
1382             mChart.bindNetworkPolicy(null);
1383         }
1384
1385         mDataEnabledView.setVisibility(dataEnabledVisible ? View.VISIBLE : View.GONE);
1386         mDisableAtLimitView.setVisibility(disableAtLimitVisible ? View.VISIBLE : View.GONE);
1387
1388         if (refreshCycle) {
1389             // generate cycle list based on policy and available history
1390             updateCycleList(policy);
1391         }
1392     }
1393
1394     /**
1395      * Rebuild {@link #mCycleAdapter} based on {@link NetworkPolicy#cycleDay}
1396      * and available {@link NetworkStatsHistory} data. Always selects the newest
1397      * item, updating the inspection range on {@link #mChart}.
1398      */
1399     private void updateCycleList(NetworkPolicy policy) {
1400         // stash away currently selected cycle to try restoring below
1401         final CycleItem previousItem = (CycleItem) mCycleSpinner.getSelectedItem();
1402         mCycleAdapter.clear();
1403
1404         final Context context = mCycleSpinner.getContext();
1405         NetworkStatsHistory.Entry entry = null;
1406
1407         long historyStart = Long.MAX_VALUE;
1408         long historyEnd = Long.MIN_VALUE;
1409         if (mChartData != null) {
1410             historyStart = mChartData.network.getStart();
1411             historyEnd = mChartData.network.getEnd();
1412         }
1413
1414         final long now = System.currentTimeMillis();
1415         if (historyStart == Long.MAX_VALUE) historyStart = now;
1416         if (historyEnd == Long.MIN_VALUE) historyEnd = now + 1;
1417
1418         boolean hasCycles = false;
1419         if (policy != null) {
1420             // find the next cycle boundary
1421             long cycleEnd = computeNextCycleBoundary(historyEnd, policy);
1422
1423             // walk backwards, generating all valid cycle ranges
1424             while (cycleEnd > historyStart) {
1425                 final long cycleStart = computeLastCycleBoundary(cycleEnd, policy);
1426                 Log.d(TAG, "generating cs=" + cycleStart + " to ce=" + cycleEnd + " waiting for hs="
1427                         + historyStart);
1428
1429                 final boolean includeCycle;
1430                 if (mChartData != null) {
1431                     entry = mChartData.network.getValues(cycleStart, cycleEnd, entry);
1432                     includeCycle = (entry.rxBytes + entry.txBytes) > 0;
1433                 } else {
1434                     includeCycle = true;
1435                 }
1436
1437                 if (includeCycle) {
1438                     mCycleAdapter.add(new CycleItem(context, cycleStart, cycleEnd));
1439                     hasCycles = true;
1440                 }
1441                 cycleEnd = cycleStart;
1442             }
1443
1444             // one last cycle entry to modify policy cycle day
1445             mCycleAdapter.setChangePossible(isNetworkPolicyModifiable(policy));
1446         }
1447
1448         if (!hasCycles) {
1449             // no policy defined cycles; show entry for each four-week period
1450             long cycleEnd = historyEnd;
1451             while (cycleEnd > historyStart) {
1452                 final long cycleStart = cycleEnd - (DateUtils.WEEK_IN_MILLIS * 4);
1453
1454                 final boolean includeCycle;
1455                 if (mChartData != null) {
1456                     entry = mChartData.network.getValues(cycleStart, cycleEnd, entry);
1457                     includeCycle = (entry.rxBytes + entry.txBytes) > 0;
1458                 } else {
1459                     includeCycle = true;
1460                 }
1461
1462                 if (includeCycle) {
1463                     mCycleAdapter.add(new CycleItem(context, cycleStart, cycleEnd));
1464                 }
1465                 cycleEnd = cycleStart;
1466             }
1467
1468             mCycleAdapter.setChangePossible(false);
1469         }
1470
1471         // force pick the current cycle (first item)
1472         if (mCycleAdapter.getCount() > 0) {
1473             final int position = mCycleAdapter.findNearestPosition(previousItem);
1474             mCycleSpinner.setSelection(position);
1475
1476             // only force-update cycle when changed; skipping preserves any
1477             // user-defined inspection region.
1478             final CycleItem selectedItem = mCycleAdapter.getItem(position);
1479             if (!Objects.equal(selectedItem, previousItem)) {
1480                 mCycleListener.onItemSelected(mCycleSpinner, null, position, 0);
1481             } else {
1482                 // but still kick off loader for detailed list
1483                 updateDetailData();
1484             }
1485         } else {
1486             updateDetailData();
1487         }
1488     }
1489
1490     private View.OnClickListener mDataEnabledListener = new View.OnClickListener() {
1491         @Override
1492         public void onClick(View v) {
1493             if (mBinding) return;
1494
1495             final boolean enabled = !mDataEnabled.isChecked();
1496             final String currentTab = mCurrentTab;
1497             if (isMobileTab(currentTab)) {
1498                 MetricsLogger.action(getContext(), MetricsLogger.ACTION_CELL_DATA_TOGGLE, enabled);
1499                 if (enabled) {
1500                     setMobileDataEnabled(getSubId(currentTab), true);
1501                 } else {
1502                     // disabling data; show confirmation dialog which eventually
1503                     // calls setMobileDataEnabled() once user confirms.
1504                     ConfirmDataDisableFragment.show(DataUsageSummary.this, getSubId(mCurrentTab));
1505                 }
1506             }
1507
1508             updatePolicy(false);
1509         }
1510     };
1511
1512     private View.OnClickListener mDisableAtLimitListener = new View.OnClickListener() {
1513         @Override
1514         public void onClick(View v) {
1515             final boolean disableAtLimit = !mDisableAtLimit.isChecked();
1516             if (disableAtLimit) {
1517                 // enabling limit; show confirmation dialog which eventually
1518                 // calls setPolicyLimitBytes() once user confirms.
1519                 ConfirmLimitFragment.show(DataUsageSummary.this);
1520             } else {
1521                 setPolicyLimitBytes(LIMIT_DISABLED);
1522             }
1523         }
1524     };
1525
1526     private AdapterView.OnItemSelectedListener mAppRestrictListener =
1527             new AdapterView.OnItemSelectedListener() {
1528         @Override
1529         public void onItemSelected(AdapterView<?> parent, View v, int position, long id) {
1530             final int backgroundPolicy;
1531
1532             if (position == DATA_USAGE_BACKGROUND_WLAN_ACCESS) {
1533                 backgroundPolicy = POLICY_REJECT_METERED_BACKGROUND;
1534             } else if (position == DATA_USAGE_BACKGROUND_NO_ACCESS) {
1535                 backgroundPolicy = POLICY_REJECT_METERED_BACKGROUND |
1536                         POLICY_REJECT_ON_WLAN_BACKGROUND;
1537             } else {
1538                 backgroundPolicy = DATA_USAGE_BACKGROUND_FULL_ACCESS;
1539             }
1540             setAppRestrictBackground(backgroundPolicy);
1541         }
1542
1543         @Override
1544         public void onNothingSelected(AdapterView<?> parent) {
1545             // noop
1546         }
1547     };
1548
1549     private View.OnClickListener mAppRestrictCellularListener = new View.OnClickListener() {
1550         @Override
1551         public void onClick(View v) {
1552             final boolean restrictCellular = !mAppCellularAccess.isChecked();
1553
1554             if (restrictCellular) {
1555                 // enabling restriction; show confirmation dialog which
1556                 // eventually calls setRestrictCellular() once user
1557                 // confirms.
1558                 ConfirmAppRestrictCellularFragment.show(DataUsageSummary.this);
1559             } else {
1560                 setAppRestrictCellular(false);
1561             }
1562         }
1563     };
1564
1565     private View.OnClickListener mAppDataAlertListner = new View.OnClickListener() {
1566         @Override
1567         public void onClick(View v) {
1568             final boolean enableDataAlert = !mAppDataAlert.isChecked();
1569             setAppDataAlert(enableDataAlert);
1570         }
1571     };
1572
1573     private OnItemClickListener mListListener = new OnItemClickListener() {
1574         @Override
1575         public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
1576             final Context context = view.getContext();
1577             final AppItem app = (AppItem) parent.getItemAtPosition(position);
1578
1579             // TODO: sigh, remove this hack once we understand 6450986
1580             if (mUidDetailProvider == null || app == null) return;
1581
1582             final UidDetail detail = mUidDetailProvider.getUidDetail(app.key, true);
1583             AppDetailsFragment.show(DataUsageSummary.this, app, detail.label);
1584         }
1585     };
1586
1587     private OnItemSelectedListener mCycleListener = new OnItemSelectedListener() {
1588         @Override
1589         public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
1590             final CycleItem cycle = (CycleItem) parent.getItemAtPosition(position);
1591             if (cycle instanceof CycleChangeItem) {
1592                 // show cycle editor; will eventually call setPolicyCycleDay()
1593                 // when user finishes editing.
1594                 CycleEditorFragment.show(DataUsageSummary.this);
1595
1596                 // reset spinner to something other than "change cycle..."
1597                 mCycleSpinner.setSelection(0);
1598
1599             } else {
1600                 if (LOGD) {
1601                     Log.d(TAG, "showing cycle " + cycle + ", start=" + cycle.start + ", end="
1602                             + cycle.end + "]");
1603                 }
1604
1605                 // update chart to show selected cycle, and update detail data
1606                 // to match updated sweep bounds.
1607                 mChart.setVisibleRange(cycle.start, cycle.end);
1608
1609                 updateDetailData();
1610             }
1611         }
1612
1613         @Override
1614         public void onNothingSelected(AdapterView<?> parent) {
1615             // ignored
1616         }
1617     };
1618
1619     /**
1620      * Update details based on {@link #mChart} inspection range depending on
1621      * current mode. In network mode, updates {@link #mAdapter} with sorted list
1622      * of applications data usage, and when {@link #isAppDetailMode()} update
1623      * app details.
1624      */
1625     private void updateDetailData() {
1626         if (LOGD) Log.d(TAG, "updateDetailData()");
1627
1628         final long start = mChart.getInspectStart();
1629         final long end = mChart.getInspectEnd();
1630         final long now = System.currentTimeMillis();
1631
1632         final Context context = getActivity();
1633
1634         NetworkStatsHistory.Entry entry = null;
1635         if (isAppDetailMode() && mChartData != null && mChartData.detail != null) {
1636             // bind foreground/background to piechart and labels
1637             entry = mChartData.detailDefault.getValues(start, end, now, entry);
1638             final long defaultBytes = entry.rxBytes + entry.txBytes;
1639             entry = mChartData.detailForeground.getValues(start, end, now, entry);
1640             final long foregroundBytes = entry.rxBytes + entry.txBytes;
1641             final long totalBytes = defaultBytes + foregroundBytes;
1642
1643             if (mAppTotal != null) {
1644                 mAppTotal.setText(Formatter.formatFileSize(context, totalBytes));
1645             }
1646             mAppBackground.setText(Formatter.formatFileSize(context, defaultBytes));
1647             mAppForeground.setText(Formatter.formatFileSize(context, foregroundBytes));
1648
1649             // and finally leave with summary data for label below
1650             entry = mChartData.detail.getValues(start, end, now, null);
1651
1652             getLoaderManager().destroyLoader(LOADER_SUMMARY);
1653
1654             mCycleSummary.setVisibility(View.GONE);
1655
1656         } else {
1657             if (mChartData != null) {
1658                 entry = mChartData.network.getValues(start, end, now, null);
1659             }
1660
1661             mCycleSummary.setVisibility(View.VISIBLE);
1662
1663             // kick off loader for detailed stats
1664             getLoaderManager().restartLoader(LOADER_SUMMARY,
1665                     SummaryForAllUidLoader.buildArgs(mTemplate, start, end), mSummaryCallbacks);
1666         }
1667
1668         final long totalBytes = entry != null ? entry.rxBytes + entry.txBytes : 0;
1669         final String totalPhrase = Formatter.formatFileSize(context, totalBytes);
1670         mCycleSummary.setText(totalPhrase);
1671
1672         if (isMobileTab(mCurrentTab) || TAB_3G.equals(mCurrentTab)
1673                 || TAB_4G.equals(mCurrentTab)) {
1674             if (isAppDetailMode()) {
1675                 mDisclaimer.setVisibility(View.GONE);
1676             } else {
1677                 mDisclaimer.setVisibility(View.VISIBLE);
1678             }
1679         } else {
1680             mDisclaimer.setVisibility(View.GONE);
1681         }
1682
1683         // initial layout is finished above, ensure we have transitions
1684         ensureLayoutTransitions();
1685     }
1686
1687     private final LoaderCallbacks<ChartData> mChartDataCallbacks = new LoaderCallbacks<
1688             ChartData>() {
1689         @Override
1690         public Loader<ChartData> onCreateLoader(int id, Bundle args) {
1691             return new ChartDataLoader(getActivity(), mStatsSession, args);
1692         }
1693
1694         @Override
1695         public void onLoadFinished(Loader<ChartData> loader, ChartData data) {
1696             mChartData = data;
1697             mChart.bindNetworkStats(mChartData.network);
1698             mChart.bindDetailNetworkStats(mChartData.detail);
1699
1700             // calcuate policy cycles based on available data
1701             updatePolicy(true);
1702             updateAppDetail();
1703
1704             // force scroll to top of body when showing detail
1705             if (mChartData.detail != null) {
1706                 mListView.smoothScrollToPosition(0);
1707             }
1708         }
1709
1710         @Override
1711         public void onLoaderReset(Loader<ChartData> loader) {
1712             mChartData = null;
1713             mChart.bindNetworkStats(null);
1714             mChart.bindDetailNetworkStats(null);
1715         }
1716     };
1717
1718     private final LoaderCallbacks<NetworkStats> mSummaryCallbacks = new LoaderCallbacks<
1719             NetworkStats>() {
1720         @Override
1721         public Loader<NetworkStats> onCreateLoader(int id, Bundle args) {
1722             return new SummaryForAllUidLoader(getActivity(), mStatsSession, args);
1723         }
1724
1725         @Override
1726         public void onLoadFinished(Loader<NetworkStats> loader, NetworkStats data) {
1727             final int[] restrictedUids = mPolicyManager.getUidsWithPolicy(
1728                     POLICY_REJECT_METERED_BACKGROUND);
1729             mAdapter.bindStats(data, restrictedUids);
1730             updateEmptyVisible();
1731         }
1732
1733         @Override
1734         public void onLoaderReset(Loader<NetworkStats> loader) {
1735             mAdapter.bindStats(null, new int[0]);
1736             updateEmptyVisible();
1737         }
1738
1739         private void updateEmptyVisible() {
1740             final boolean isEmpty = mAdapter.isEmpty() && !isAppDetailMode();
1741             mEmpty.setVisibility(isEmpty ? View.VISIBLE : View.GONE);
1742             mStupidPadding.setVisibility(isEmpty ? View.VISIBLE : View.GONE);
1743         }
1744     };
1745
1746     private static String getActiveSubscriberId(Context context) {
1747         final TelephonyManager tele = TelephonyManager.from(context);
1748         final String actualSubscriberId = tele.getSubscriberId();
1749         String retVal = SystemProperties.get(TEST_SUBSCRIBER_PROP, actualSubscriberId);
1750         if (LOGD) Log.d(TAG, "getActiveSubscriberId=" + retVal + " actualSubscriberId=" + actualSubscriberId);
1751         return retVal;
1752     }
1753
1754     private static String getActiveSubscriberId(Context context, int subId) {
1755         final TelephonyManager tele = TelephonyManager.from(context);
1756         String retVal = tele.getSubscriberId(subId);
1757         if (LOGD) Log.d(TAG, "getActiveSubscriberId=" + retVal + " subId=" + subId);
1758         return retVal;
1759     }
1760
1761     private DataUsageChartListener mChartListener = new DataUsageChartListener() {
1762         @Override
1763         public void onWarningChanged() {
1764             setPolicyWarningBytes(mChart.getWarningBytes());
1765         }
1766
1767         @Override
1768         public void onLimitChanged() {
1769             setPolicyLimitBytes(mChart.getLimitBytes());
1770             updateBody();
1771         }
1772
1773         @Override
1774         public void requestWarningEdit() {
1775             WarningEditorFragment.show(DataUsageSummary.this);
1776         }
1777
1778         @Override
1779         public void requestLimitEdit() {
1780             LimitEditorFragment.show(DataUsageSummary.this);
1781         }
1782     };
1783
1784     /**
1785      * List item that reflects a specific data usage cycle.
1786      */
1787     public static class CycleItem implements Comparable<CycleItem> {
1788         public CharSequence label;
1789         public long start;
1790         public long end;
1791
1792         CycleItem(CharSequence label) {
1793             this.label = label;
1794         }
1795
1796         public CycleItem(Context context, long start, long end) {
1797             this.label = formatDateRange(context, start, end);
1798             this.start = start;
1799             this.end = end;
1800         }
1801
1802         @Override
1803         public String toString() {
1804             return label.toString();
1805         }
1806
1807         @Override
1808         public boolean equals(Object o) {
1809             if (o instanceof CycleItem) {
1810                 final CycleItem another = (CycleItem) o;
1811                 return start == another.start && end == another.end;
1812             }
1813             return false;
1814         }
1815
1816         @Override
1817         public int compareTo(CycleItem another) {
1818             return Long.compare(start, another.start);
1819         }
1820     }
1821
1822     private static final StringBuilder sBuilder = new StringBuilder(50);
1823     private static final java.util.Formatter sFormatter = new java.util.Formatter(
1824             sBuilder, Locale.getDefault());
1825
1826     public static String formatDateRange(Context context, long start, long end) {
1827         final int flags = FORMAT_SHOW_DATE | FORMAT_ABBREV_MONTH;
1828
1829         synchronized (sBuilder) {
1830             sBuilder.setLength(0);
1831             return DateUtils.formatDateRange(context, sFormatter, start, end, flags, null)
1832                     .toString();
1833         }
1834     }
1835
1836     /**
1837      * Special-case data usage cycle that triggers dialog to change
1838      * {@link NetworkPolicy#cycleDay}.
1839      */
1840     public static class CycleChangeItem extends CycleItem {
1841         public CycleChangeItem(Context context) {
1842             super(context.getString(R.string.data_usage_change_cycle));
1843         }
1844     }
1845
1846     public static class CycleAdapter extends ArrayAdapter<CycleItem> {
1847         private boolean mChangePossible = false;
1848         private boolean mChangeVisible = false;
1849
1850         private final CycleChangeItem mChangeItem;
1851
1852         public CycleAdapter(Context context) {
1853             super(context, R.layout.data_usage_cycle_item);
1854             setDropDownViewResource(R.layout.data_usage_cycle_item_dropdown);
1855             mChangeItem = new CycleChangeItem(context);
1856         }
1857
1858         public void setChangePossible(boolean possible) {
1859             mChangePossible = possible;
1860             updateChange();
1861         }
1862
1863         public void setChangeVisible(boolean visible) {
1864             mChangeVisible = visible;
1865             updateChange();
1866         }
1867
1868         private void updateChange() {
1869             remove(mChangeItem);
1870             if (mChangePossible && mChangeVisible) {
1871                 add(mChangeItem);
1872             }
1873         }
1874
1875         /**
1876          * Find position of {@link CycleItem} in this adapter which is nearest
1877          * the given {@link CycleItem}.
1878          */
1879         public int findNearestPosition(CycleItem target) {
1880             if (target != null) {
1881                 final int count = getCount();
1882                 for (int i = count - 1; i >= 0; i--) {
1883                     final CycleItem item = getItem(i);
1884                     if (item instanceof CycleChangeItem) {
1885                         continue;
1886                     } else if (item.compareTo(target) >= 0) {
1887                         return i;
1888                     }
1889                 }
1890             }
1891             return 0;
1892         }
1893     }
1894
1895     /**
1896      * Adapter of applications, sorted by total usage descending.
1897      */
1898     public static class DataUsageAdapter extends BaseAdapter {
1899         private final UidDetailProvider mProvider;
1900         private final int mInsetSide;
1901         private final UserManager mUm;
1902
1903         private ArrayList<AppItem> mItems = Lists.newArrayList();
1904         private long mLargest;
1905
1906         public DataUsageAdapter(final UserManager userManager, UidDetailProvider provider, int insetSide) {
1907             mProvider = checkNotNull(provider);
1908             mInsetSide = insetSide;
1909             mUm = userManager;
1910         }
1911
1912         /**
1913          * Bind the given {@link NetworkStats}, or {@code null} to clear list.
1914          */
1915         public void bindStats(NetworkStats stats, int[] restrictedUids) {
1916             mItems.clear();
1917             mLargest = 0;
1918
1919             final int currentUserId = ActivityManager.getCurrentUser();
1920             final List<UserHandle> profiles = mUm.getUserProfiles();
1921             final SparseArray<AppItem> knownItems = new SparseArray<AppItem>();
1922
1923             NetworkStats.Entry entry = null;
1924             final int size = stats != null ? stats.size() : 0;
1925             for (int i = 0; i < size; i++) {
1926                 entry = stats.getValues(i, entry);
1927
1928                 // Decide how to collapse items together
1929                 final int uid = entry.uid;
1930
1931                 final int collapseKey;
1932                 final int category;
1933                 final int userId = UserHandle.getUserId(uid);
1934                 if (UserHandle.isApp(uid)) {
1935                     if (profiles.contains(new UserHandle(userId))) {
1936                         if (userId != currentUserId) {
1937                             // Add to a managed user item.
1938                             final int managedKey = UidDetailProvider.buildKeyForUser(userId);
1939                             accumulate(managedKey, knownItems, entry,
1940                                     AppItem.CATEGORY_USER);
1941                         }
1942                         // Add to app item.
1943                         collapseKey = uid;
1944                         category = AppItem.CATEGORY_APP;
1945                     } else {
1946                         // If it is a removed user add it to the removed users' key
1947                         final UserInfo info = mUm.getUserInfo(userId);
1948                         if (info == null) {
1949                             collapseKey = UID_REMOVED;
1950                             category = AppItem.CATEGORY_APP;
1951                         } else {
1952                             // Add to other user item.
1953                             collapseKey = UidDetailProvider.buildKeyForUser(userId);
1954                             category = AppItem.CATEGORY_USER;
1955                         }
1956                     }
1957                 } else if (uid == UID_REMOVED || uid == UID_TETHERING) {
1958                     collapseKey = uid;
1959                     category = AppItem.CATEGORY_APP;
1960                 } else {
1961                     collapseKey = android.os.Process.SYSTEM_UID;
1962                     category = AppItem.CATEGORY_APP;
1963                 }
1964                 accumulate(collapseKey, knownItems, entry, category);
1965             }
1966
1967             final int restrictedUidsMax = restrictedUids.length;
1968             for (int i = 0; i < restrictedUidsMax; ++i) {
1969                 final int uid = restrictedUids[i];
1970                 // Only splice in restricted state for current user or managed users
1971                 if (!profiles.contains(new UserHandle(UserHandle.getUserId(uid)))) {
1972                     continue;
1973                 }
1974
1975                 AppItem item = knownItems.get(uid);
1976                 if (item == null) {
1977                     item = new AppItem(uid);
1978                     item.total = -1;
1979                     mItems.add(item);
1980                     knownItems.put(item.key, item);
1981                 }
1982                 item.restricted = true;
1983             }
1984
1985             if (!mItems.isEmpty()) {
1986                 final AppItem title = new AppItem();
1987                 title.category = AppItem.CATEGORY_APP_TITLE;
1988                 mItems.add(title);
1989             }
1990
1991             Collections.sort(mItems);
1992             notifyDataSetChanged();
1993         }
1994
1995         /**
1996          * Accumulate data usage of a network stats entry for the item mapped by the collapse key.
1997          * Creates the item if needed.
1998          *
1999          * @param collapseKey the collapse key used to map the item.
2000          * @param knownItems collection of known (already existing) items.
2001          * @param entry the network stats entry to extract data usage from.
2002          * @param itemCategory the item is categorized on the list view by this category. Must be
2003          *            either AppItem.APP_ITEM_CATEGORY or AppItem.MANAGED_USER_ITEM_CATEGORY
2004          */
2005         private void accumulate(int collapseKey, final SparseArray<AppItem> knownItems,
2006                 NetworkStats.Entry entry, int itemCategory) {
2007             final int uid = entry.uid;
2008             AppItem item = knownItems.get(collapseKey);
2009             if (item == null) {
2010                 item = new AppItem(collapseKey);
2011                 item.category = itemCategory;
2012                 mItems.add(item);
2013                 knownItems.put(item.key, item);
2014             }
2015             item.addUid(uid);
2016             item.total += entry.rxBytes + entry.txBytes;
2017             if (mLargest < item.total) {
2018                 mLargest = item.total;
2019             }
2020         }
2021
2022         @Override
2023         public int getCount() {
2024             return mItems.size();
2025         }
2026
2027         @Override
2028         public Object getItem(int position) {
2029             return mItems.get(position);
2030         }
2031
2032         @Override
2033         public long getItemId(int position) {
2034             return mItems.get(position).key;
2035         }
2036
2037         /**
2038          * See {@link #getItemViewType} for the view types.
2039          */
2040         @Override
2041         public int getViewTypeCount() {
2042             return 2;
2043         }
2044
2045         /**
2046          * Returns 1 for separator items and 0 for anything else.
2047          */
2048         @Override
2049         public int getItemViewType(int position) {
2050             final AppItem item = mItems.get(position);
2051             if (item.category == AppItem.CATEGORY_APP_TITLE) {
2052                 return 1;
2053             } else {
2054                 return 0;
2055             }
2056         }
2057
2058         @Override
2059         public boolean areAllItemsEnabled() {
2060             return false;
2061         }
2062
2063         @Override
2064         public boolean isEnabled(int position) {
2065             if (position > mItems.size()) {
2066                 throw new ArrayIndexOutOfBoundsException();
2067             }
2068             return getItemViewType(position) == 0;
2069         }
2070
2071         @Override
2072         public View getView(int position, View convertView, ViewGroup parent) {
2073             final AppItem item = mItems.get(position);
2074             LayoutInflater inflater = LayoutInflater.from(parent.getContext());
2075             if (getItemViewType(position) == 1) {
2076                 if (convertView == null) {
2077                     convertView = Utils.inflateCategoryHeader(inflater, parent);
2078                 }
2079
2080                 final TextView title = (TextView) convertView.findViewById(android.R.id.title);
2081                 title.setText(R.string.data_usage_app);
2082
2083             } else {
2084                 if (convertView == null) {
2085                     convertView = inflater.inflate(R.layout.data_usage_item, parent, false);
2086                     inflater.inflate(R.layout.widget_progress_bar,
2087                             (ViewGroup) convertView.findViewById(android.R.id.widget_frame));
2088
2089                     if (mInsetSide > 0) {
2090                         convertView.setPaddingRelative(mInsetSide, 0, mInsetSide, 0);
2091                     }
2092                 }
2093
2094                 final Context context = parent.getContext();
2095
2096                 final TextView summary = (TextView) convertView.findViewById(android.R.id.summary);
2097                 final ProgressBar progress = (ProgressBar) convertView.findViewById(
2098                         android.R.id.progress);
2099
2100                 // kick off async load of app details
2101                 UidDetailTask.bindView(mProvider, item, convertView);
2102
2103                 if (item.restricted && item.total <= 0) {
2104                     summary.setText(R.string.data_usage_app_restricted);
2105                     progress.setVisibility(View.GONE);
2106                 } else {
2107                     summary.setText(Formatter.formatFileSize(context, item.total));
2108                     progress.setVisibility(View.VISIBLE);
2109                 }
2110
2111                 final int percentTotal = mLargest != 0 ? (int) (item.total * 100 / mLargest) : 0;
2112                 progress.setProgress(percentTotal);
2113             }
2114
2115             return convertView;
2116         }
2117     }
2118
2119     /**
2120      * Empty {@link Fragment} that controls display of UID details in
2121      * {@link DataUsageSummary}.
2122      */
2123     public static class AppDetailsFragment extends Fragment {
2124         private static final String EXTRA_APP = "app";
2125
2126         public static void show(DataUsageSummary parent, AppItem app, CharSequence label) {
2127             show(parent, app, label, true);
2128         }
2129
2130         public static void show(DataUsageSummary parent, AppItem app, CharSequence label,
2131                 boolean addToBack) {
2132             if (!parent.isAdded()) return;
2133
2134             final Bundle args = new Bundle();
2135             args.putParcelable(EXTRA_APP, app);
2136
2137             final AppDetailsFragment fragment = new AppDetailsFragment();
2138             fragment.setArguments(args);
2139             fragment.setTargetFragment(parent, 0);
2140             final FragmentTransaction ft = parent.getFragmentManager().beginTransaction();
2141             ft.add(fragment, TAG_APP_DETAILS);
2142             if (addToBack) {
2143                 ft.addToBackStack(TAG_APP_DETAILS);
2144             }
2145             ft.setBreadCrumbTitle(
2146                     parent.getResources().getString(R.string.data_usage_app_summary_title));
2147             ft.commitAllowingStateLoss();
2148         }
2149
2150         @Override
2151         public void onStart() {
2152             super.onStart();
2153             final DataUsageSummary target = (DataUsageSummary) getTargetFragment();
2154             target.mCurrentApp = getArguments().getParcelable(EXTRA_APP);
2155             target.updateBody();
2156         }
2157
2158         @Override
2159         public void onStop() {
2160             super.onStop();
2161             final DataUsageSummary target = (DataUsageSummary) getTargetFragment();
2162             target.mCurrentApp = null;
2163             target.updateBody();
2164         }
2165     }
2166
2167     /**
2168      * Dialog to request user confirmation before setting
2169      * {@link NetworkPolicy#limitBytes}.
2170      */
2171     public static class ConfirmLimitFragment extends DialogFragment {
2172         private static final String EXTRA_MESSAGE = "message";
2173         private static final String EXTRA_LIMIT_BYTES = "limitBytes";
2174
2175         public static void show(DataUsageSummary parent) {
2176             if (!parent.isAdded()) return;
2177
2178             final NetworkPolicy policy = parent.mPolicyEditor.getPolicy(parent.mTemplate);
2179             if (policy == null) return;
2180
2181             final Resources res = parent.getResources();
2182             final CharSequence message;
2183             final long minLimitBytes = (long) (policy.warningBytes * 1.2f);
2184             final long limitBytes;
2185
2186             // TODO: customize default limits based on network template
2187             final String currentTab = parent.mCurrentTab;
2188             if (TAB_3G.equals(currentTab)) {
2189                 message = res.getString(R.string.data_usage_limit_dialog_mobile);
2190                 limitBytes = Math.max(5 * GB_IN_BYTES, minLimitBytes);
2191             } else if (TAB_4G.equals(currentTab)) {
2192                 message = res.getString(R.string.data_usage_limit_dialog_mobile);
2193                 limitBytes = Math.max(5 * GB_IN_BYTES, minLimitBytes);
2194             } else if (isMobileTab(currentTab)) {
2195                 message = res.getString(R.string.data_usage_limit_dialog_mobile);
2196                 limitBytes = Math.max(5 * GB_IN_BYTES, minLimitBytes);
2197             } else {
2198                 throw new IllegalArgumentException("unknown current tab: " + currentTab);
2199             }
2200
2201             final Bundle args = new Bundle();
2202             args.putCharSequence(EXTRA_MESSAGE, message);
2203             args.putLong(EXTRA_LIMIT_BYTES, limitBytes);
2204
2205             final ConfirmLimitFragment dialog = new ConfirmLimitFragment();
2206             dialog.setArguments(args);
2207             dialog.setTargetFragment(parent, 0);
2208             dialog.show(parent.getFragmentManager(), TAG_CONFIRM_LIMIT);
2209         }
2210
2211         @Override
2212         public Dialog onCreateDialog(Bundle savedInstanceState) {
2213             final Context context = getActivity();
2214
2215             final CharSequence message = getArguments().getCharSequence(EXTRA_MESSAGE);
2216             final long limitBytes = getArguments().getLong(EXTRA_LIMIT_BYTES);
2217
2218             final AlertDialog.Builder builder = new AlertDialog.Builder(context);
2219             builder.setTitle(R.string.data_usage_limit_dialog_title);
2220             builder.setMessage(message);
2221
2222             builder.setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() {
2223                 @Override
2224                 public void onClick(DialogInterface dialog, int which) {
2225                     final DataUsageSummary target = (DataUsageSummary) getTargetFragment();
2226                     if (target != null) {
2227                         target.setPolicyLimitBytes(limitBytes);
2228                     }
2229                 }
2230             });
2231
2232             return builder.create();
2233         }
2234     }
2235
2236     /**
2237      * Dialog to edit {@link NetworkPolicy#cycleDay}.
2238      */
2239     public static class CycleEditorFragment extends DialogFragment {
2240         private static final String EXTRA_TEMPLATE = "template";
2241
2242         public static void show(DataUsageSummary parent) {
2243             if (!parent.isAdded()) return;
2244
2245             final Bundle args = new Bundle();
2246             args.putParcelable(EXTRA_TEMPLATE, parent.mTemplate);
2247
2248             final CycleEditorFragment dialog = new CycleEditorFragment();
2249             dialog.setArguments(args);
2250             dialog.setTargetFragment(parent, 0);
2251             dialog.show(parent.getFragmentManager(), TAG_CYCLE_EDITOR);
2252         }
2253
2254         @Override
2255         public Dialog onCreateDialog(Bundle savedInstanceState) {
2256             final Context context = getActivity();
2257             final DataUsageSummary target = (DataUsageSummary) getTargetFragment();
2258             final NetworkPolicyEditor editor = target.mPolicyEditor;
2259
2260             final AlertDialog.Builder builder = new AlertDialog.Builder(context);
2261             final LayoutInflater dialogInflater = LayoutInflater.from(builder.getContext());
2262
2263             final View view = dialogInflater.inflate(R.layout.data_usage_cycle_editor, null, false);
2264             final NumberPicker cycleDayPicker = (NumberPicker) view.findViewById(R.id.cycle_day);
2265
2266             final NetworkTemplate template = getArguments().getParcelable(EXTRA_TEMPLATE);
2267             final int cycleDay = editor.getPolicyCycleDay(template);
2268
2269             cycleDayPicker.setMinValue(1);
2270             cycleDayPicker.setMaxValue(31);
2271             cycleDayPicker.setValue(cycleDay);
2272             cycleDayPicker.setWrapSelectorWheel(true);
2273
2274             builder.setTitle(R.string.data_usage_cycle_editor_title);
2275             builder.setView(view);
2276
2277             builder.setPositiveButton(R.string.data_usage_cycle_editor_positive,
2278                     new DialogInterface.OnClickListener() {
2279                         @Override
2280                         public void onClick(DialogInterface dialog, int which) {
2281                             // clear focus to finish pending text edits
2282                             cycleDayPicker.clearFocus();
2283
2284                             final int cycleDay = cycleDayPicker.getValue();
2285                             final String cycleTimezone = new Time().timezone;
2286                             editor.setPolicyCycleDay(template, cycleDay, cycleTimezone);
2287                             target.updatePolicy(true);
2288                         }
2289                     });
2290
2291             return builder.create();
2292         }
2293     }
2294
2295     /**
2296      * Dialog to edit {@link NetworkPolicy#warningBytes}.
2297      */
2298     public static class WarningEditorFragment extends DialogFragment {
2299         private static final String EXTRA_TEMPLATE = "template";
2300
2301         public static void show(DataUsageSummary parent) {
2302             if (!parent.isAdded()) return;
2303
2304             final Bundle args = new Bundle();
2305             args.putParcelable(EXTRA_TEMPLATE, parent.mTemplate);
2306
2307             final WarningEditorFragment dialog = new WarningEditorFragment();
2308             dialog.setArguments(args);
2309             dialog.setTargetFragment(parent, 0);
2310             dialog.show(parent.getFragmentManager(), TAG_WARNING_EDITOR);
2311         }
2312
2313         @Override
2314         public Dialog onCreateDialog(Bundle savedInstanceState) {
2315             final Context context = getActivity();
2316             final DataUsageSummary target = (DataUsageSummary) getTargetFragment();
2317             final NetworkPolicyEditor editor = target.mPolicyEditor;
2318
2319             final AlertDialog.Builder builder = new AlertDialog.Builder(context);
2320             final LayoutInflater dialogInflater = LayoutInflater.from(builder.getContext());
2321
2322             final View view = dialogInflater.inflate(R.layout.data_usage_bytes_editor, null, false);
2323             final NumberPicker bytesPicker = (NumberPicker) view.findViewById(R.id.bytes);
2324
2325             final NetworkTemplate template = getArguments().getParcelable(EXTRA_TEMPLATE);
2326             final long warningBytes = editor.getPolicyWarningBytes(template);
2327             final long limitBytes = editor.getPolicyLimitBytes(template);
2328
2329             bytesPicker.setMinValue(0);
2330             if (limitBytes != LIMIT_DISABLED) {
2331                 bytesPicker.setMaxValue((int) (limitBytes / MB_IN_BYTES) - 1);
2332             } else {
2333                 bytesPicker.setMaxValue(Integer.MAX_VALUE);
2334             }
2335             bytesPicker.setValue((int) (warningBytes / MB_IN_BYTES));
2336             bytesPicker.setWrapSelectorWheel(false);
2337
2338             builder.setTitle(R.string.data_usage_warning_editor_title);
2339             builder.setView(view);
2340
2341             builder.setPositiveButton(R.string.data_usage_cycle_editor_positive,
2342                     new DialogInterface.OnClickListener() {
2343                         @Override
2344                         public void onClick(DialogInterface dialog, int which) {
2345                             // clear focus to finish pending text edits
2346                             bytesPicker.clearFocus();
2347
2348                             final long bytes = bytesPicker.getValue() * MB_IN_BYTES;
2349                             editor.setPolicyWarningBytes(template, bytes);
2350                             target.updatePolicy(false);
2351                         }
2352                     });
2353
2354             return builder.create();
2355         }
2356     }
2357
2358     /**
2359      * Dialog to edit {@link NetworkPolicy#limitBytes}.
2360      */
2361     public static class LimitEditorFragment extends DialogFragment {
2362         private static final String EXTRA_TEMPLATE = "template";
2363
2364         public static void show(DataUsageSummary parent) {
2365             if (!parent.isAdded()) return;
2366
2367             final Bundle args = new Bundle();
2368             args.putParcelable(EXTRA_TEMPLATE, parent.mTemplate);
2369
2370             final LimitEditorFragment dialog = new LimitEditorFragment();
2371             dialog.setArguments(args);
2372             dialog.setTargetFragment(parent, 0);
2373             dialog.show(parent.getFragmentManager(), TAG_LIMIT_EDITOR);
2374         }
2375
2376         @Override
2377         public Dialog onCreateDialog(Bundle savedInstanceState) {
2378             final Context context = getActivity();
2379             final DataUsageSummary target = (DataUsageSummary) getTargetFragment();
2380             final NetworkPolicyEditor editor = target.mPolicyEditor;
2381
2382             final AlertDialog.Builder builder = new AlertDialog.Builder(context);
2383             final LayoutInflater dialogInflater = LayoutInflater.from(builder.getContext());
2384
2385             final View view = dialogInflater.inflate(R.layout.data_usage_bytes_editor, null, false);
2386             final NumberPicker bytesPicker = (NumberPicker) view.findViewById(R.id.bytes);
2387
2388             final NetworkTemplate template = getArguments().getParcelable(EXTRA_TEMPLATE);
2389             final long warningBytes = editor.getPolicyWarningBytes(template);
2390             final long limitBytes = editor.getPolicyLimitBytes(template);
2391
2392             bytesPicker.setMaxValue(Integer.MAX_VALUE);
2393             if (warningBytes != WARNING_DISABLED && limitBytes > 0) {
2394                 bytesPicker.setMinValue((int) (warningBytes / MB_IN_BYTES) + 1);
2395             } else {
2396                 bytesPicker.setMinValue(0);
2397             }
2398             bytesPicker.setValue((int) (limitBytes / MB_IN_BYTES));
2399             bytesPicker.setWrapSelectorWheel(false);
2400
2401             builder.setTitle(R.string.data_usage_limit_editor_title);
2402             builder.setView(view);
2403
2404             builder.setPositiveButton(R.string.data_usage_cycle_editor_positive,
2405                     new DialogInterface.OnClickListener() {
2406                         @Override
2407                         public void onClick(DialogInterface dialog, int which) {
2408                             // clear focus to finish pending text edits
2409                             bytesPicker.clearFocus();
2410
2411                             final long bytes = bytesPicker.getValue() * MB_IN_BYTES;
2412                             editor.setPolicyLimitBytes(template, bytes);
2413                             target.updatePolicy(false);
2414                         }
2415                     });
2416
2417             return builder.create();
2418         }
2419     }
2420     /**
2421      * Dialog to request user confirmation before disabling data.
2422      */
2423     public static class ConfirmDataDisableFragment extends DialogFragment {
2424         static int mSubId;
2425         public static void show(DataUsageSummary parent, int subId) {
2426             mSubId = subId;
2427             if (!parent.isAdded()) return;
2428
2429             final ConfirmDataDisableFragment dialog = new ConfirmDataDisableFragment();
2430             dialog.setTargetFragment(parent, 0);
2431             dialog.show(parent.getFragmentManager(), TAG_CONFIRM_DATA_DISABLE);
2432         }
2433
2434         @Override
2435         public Dialog onCreateDialog(Bundle savedInstanceState) {
2436             final Context context = getActivity();
2437
2438             final AlertDialog.Builder builder = new AlertDialog.Builder(context);
2439             builder.setMessage(R.string.data_usage_disable_mobile);
2440
2441             builder.setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() {
2442                 @Override
2443                 public void onClick(DialogInterface dialog, int which) {
2444                     final DataUsageSummary target = (DataUsageSummary) getTargetFragment();
2445                     if (target != null) {
2446                         // TODO: extend to modify policy enabled flag.
2447                         target.setMobileDataEnabled(mSubId, false);
2448                     }
2449                 }
2450             });
2451             builder.setNegativeButton(android.R.string.cancel, null);
2452
2453             return builder.create();
2454         }
2455     }
2456
2457     /**
2458      * Dialog to request user confirmation before resetting data.
2459      */
2460     public static class ConfirmDataResetFragment extends DialogFragment {
2461         static NetworkTemplate mTemplate;
2462         public static void show(DataUsageSummary parent, NetworkTemplate template) {
2463             mTemplate = template;
2464             if (!parent.isAdded()) return;
2465
2466             final ConfirmDataResetFragment dialog = new ConfirmDataResetFragment();
2467             dialog.setTargetFragment(parent, 0);
2468             dialog.show(parent.getFragmentManager(), TAG_CONFIRM_DATA_RESET);
2469         }
2470
2471         @Override
2472         public Dialog onCreateDialog(Bundle savedInstanceState) {
2473             final Context context = getActivity();
2474
2475             final AlertDialog.Builder builder = new AlertDialog.Builder(context);
2476             builder.setTitle(R.string.data_usage_menu_reset_stats);
2477             builder.setMessage(R.string.reset_data_stats_msg);
2478
2479             builder.setPositiveButton(R.string.reset_stats_confirm,
2480                     new DialogInterface.OnClickListener() {
2481                         @Override
2482                         public void onClick(DialogInterface dialog, int which) {
2483                             final DataUsageSummary target = (DataUsageSummary) getTargetFragment();
2484                             if (target != null) {
2485                                 // TODO: extend to modify policy enabled flag.
2486                                 target.resetDataStats(mTemplate);
2487                             }
2488                         }
2489                     });
2490             builder.setNegativeButton(android.R.string.cancel, null);
2491
2492             return builder.create();
2493         }
2494     }
2495
2496     /**
2497      * Dialog to request user confirmation before setting
2498      * {@link INetworkPolicyManager#setRestrictBackground(boolean)}.
2499      */
2500     public static class ConfirmRestrictFragment extends DialogFragment {
2501         public static void show(DataUsageSummary parent) {
2502             if (!parent.isAdded()) return;
2503
2504             final ConfirmRestrictFragment dialog = new ConfirmRestrictFragment();
2505             dialog.setTargetFragment(parent, 0);
2506             dialog.show(parent.getFragmentManager(), TAG_CONFIRM_RESTRICT);
2507         }
2508
2509         @Override
2510         public Dialog onCreateDialog(Bundle savedInstanceState) {
2511             final Context context = getActivity();
2512
2513             final AlertDialog.Builder builder = new AlertDialog.Builder(context);
2514             builder.setTitle(R.string.data_usage_restrict_background_title);
2515             if (Utils.hasMultipleUsers(context)) {
2516                 builder.setMessage(R.string.data_usage_restrict_background_multiuser);
2517             } else {
2518                 builder.setMessage(R.string.data_usage_restrict_background);
2519             }
2520
2521             builder.setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() {
2522                 @Override
2523                 public void onClick(DialogInterface dialog, int which) {
2524                     final DataUsageSummary target = (DataUsageSummary) getTargetFragment();
2525                     if (target != null) {
2526                         target.setRestrictBackground(true);
2527                     }
2528                 }
2529             });
2530             builder.setNegativeButton(android.R.string.cancel, null);
2531
2532             return builder.create();
2533         }
2534     }
2535
2536     /**
2537      * Dialog to inform user that {@link #POLICY_REJECT_METERED_BACKGROUND}
2538      * change has been denied, usually based on
2539      * {@link DataUsageSummary#hasLimitedNetworks()}.
2540      */
2541     public static class DeniedRestrictFragment extends DialogFragment {
2542         public static void show(DataUsageSummary parent) {
2543             if (!parent.isAdded()) return;
2544
2545             final DeniedRestrictFragment dialog = new DeniedRestrictFragment();
2546             dialog.setTargetFragment(parent, 0);
2547             dialog.show(parent.getFragmentManager(), TAG_DENIED_RESTRICT);
2548         }
2549
2550         @Override
2551         public Dialog onCreateDialog(Bundle savedInstanceState) {
2552             final Context context = getActivity();
2553
2554             final AlertDialog.Builder builder = new AlertDialog.Builder(context);
2555             builder.setTitle(R.string.data_usage_app_restrict_background);
2556             builder.setMessage(R.string.data_usage_restrict_denied_dialog);
2557             builder.setPositiveButton(android.R.string.ok, null);
2558
2559             return builder.create();
2560         }
2561     }
2562
2563     /**
2564      * Dialog to request user confirmation before setting
2565      * {@link #POLICY_REJECT_ON_DATA}.
2566      */
2567     public static class ConfirmAppRestrictCellularFragment extends DialogFragment {
2568         public static void show(DataUsageSummary parent) {
2569             if (!parent.isAdded()) return;
2570
2571             final ConfirmAppRestrictCellularFragment dialog = new
2572                     ConfirmAppRestrictCellularFragment();
2573             dialog.setTargetFragment(parent, 0);
2574             dialog.show(parent.getFragmentManager(), TAG_CONFIRM_APP_RESTRICT_CELLULAR);
2575         }
2576
2577         @Override
2578         public Dialog onCreateDialog(Bundle savedInstanceState) {
2579             final Context context = getActivity();
2580
2581             final AlertDialog.Builder builder = new AlertDialog.Builder(context);
2582             builder.setTitle(R.string.restrict_cellular_access_dialog_title);
2583             builder.setMessage(R.string.restrict_cellular_access_dialog_summary);
2584
2585             builder.setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() {
2586                 @Override
2587                 public void onClick(DialogInterface dialog, int which) {
2588                     final DataUsageSummary target = (DataUsageSummary) getTargetFragment();
2589                     if (target != null) {
2590                         target.setAppRestrictCellular(true);
2591                     }
2592                 }
2593             });
2594             builder.setNegativeButton(android.R.string.cancel, null);
2595
2596             return builder.create();
2597         }
2598     }
2599
2600     /**
2601      * Compute default tab that should be selected, based on
2602      * {@link NetworkPolicyManager#EXTRA_NETWORK_TEMPLATE} extra.
2603      */
2604     private static String computeTabFromIntent(Intent intent) {
2605         final NetworkTemplate template = intent.getParcelableExtra(EXTRA_NETWORK_TEMPLATE);
2606         if (template == null) {
2607             final int subId = intent.getIntExtra(PhoneConstants.SUBSCRIPTION_KEY,
2608                     SubscriptionManager.INVALID_SUBSCRIPTION_ID);
2609             if (SubscriptionManager.isValidSubscriptionId(subId)) {
2610                 return TAB_MOBILE + String.valueOf(subId);
2611             }
2612             return null;
2613         }
2614
2615         switch (template.getMatchRule()) {
2616             case MATCH_MOBILE_3G_LOWER:
2617                 return TAB_3G;
2618             case MATCH_MOBILE_4G:
2619                 return TAB_4G;
2620             case MATCH_MOBILE_ALL:
2621                 return TAB_MOBILE;
2622             case MATCH_WIFI:
2623                 return TAB_WIFI;
2624             default:
2625                 return null;
2626         }
2627     }
2628
2629     /**
2630      * Background task that loads {@link UidDetail}, binding to
2631      * {@link DataUsageAdapter} row item when finished.
2632      */
2633     private static class UidDetailTask extends AsyncTask<Void, Void, UidDetail> {
2634         private final UidDetailProvider mProvider;
2635         private final AppItem mItem;
2636         private final View mTarget;
2637
2638         private UidDetailTask(UidDetailProvider provider, AppItem item, View target) {
2639             mProvider = checkNotNull(provider);
2640             mItem = checkNotNull(item);
2641             mTarget = checkNotNull(target);
2642         }
2643
2644         public static void bindView(
2645                 UidDetailProvider provider, AppItem item, View target) {
2646             final UidDetailTask existing = (UidDetailTask) target.getTag();
2647             if (existing != null) {
2648                 existing.cancel(false);
2649             }
2650
2651             final UidDetail cachedDetail = provider.getUidDetail(item.key, false);
2652             if (cachedDetail != null) {
2653                 bindView(cachedDetail, target);
2654             } else {
2655                 target.setTag(new UidDetailTask(provider, item, target).executeOnExecutor(
2656                         AsyncTask.THREAD_POOL_EXECUTOR));
2657             }
2658         }
2659
2660         private static void bindView(UidDetail detail, View target) {
2661             final ImageView icon = (ImageView) target.findViewById(android.R.id.icon);
2662             final TextView title = (TextView) target.findViewById(android.R.id.title);
2663
2664             if (detail != null) {
2665                 icon.setImageDrawable(detail.icon);
2666                 title.setText(detail.label);
2667                 title.setContentDescription(detail.contentDescription);
2668             } else {
2669                 icon.setImageDrawable(null);
2670                 title.setText(null);
2671             }
2672         }
2673
2674         @Override
2675         protected void onPreExecute() {
2676             bindView(null, mTarget);
2677         }
2678
2679         @Override
2680         protected UidDetail doInBackground(Void... params) {
2681             return mProvider.getUidDetail(mItem.key, true);
2682         }
2683
2684         @Override
2685         protected void onPostExecute(UidDetail result) {
2686             bindView(result, mTarget);
2687         }
2688     }
2689
2690     /**
2691      * Test if device has a mobile data radio with SIM in ready state.
2692      */
2693     public static boolean hasReadyMobileRadio(Context context) {
2694         if (TEST_RADIOS) {
2695             return SystemProperties.get(TEST_RADIOS_PROP).contains("mobile");
2696         }
2697
2698         final ConnectivityManager conn = ConnectivityManager.from(context);
2699         final TelephonyManager tele = TelephonyManager.from(context);
2700
2701         final List<SubscriptionInfo> subInfoList =
2702                 SubscriptionManager.from(context).getActiveSubscriptionInfoList();
2703         // No activated Subscriptions
2704         if (subInfoList == null) {
2705             if (LOGD) Log.d(TAG, "hasReadyMobileRadio: subInfoList=null");
2706             return false;
2707         }
2708         // require both supported network and ready SIM
2709         boolean isReady = true;
2710         for (SubscriptionInfo subInfo : subInfoList) {
2711             isReady = isReady & tele.getSimState(subInfo.getSimSlotIndex()) == SIM_STATE_READY;
2712             if (LOGD) Log.d(TAG, "hasReadyMobileRadio: subInfo=" + subInfo);
2713         }
2714         boolean retVal = conn.isNetworkSupported(TYPE_MOBILE) && isReady;
2715         if (LOGD) {
2716             Log.d(TAG, "hasReadyMobileRadio:"
2717                     + " conn.isNetworkSupported(TYPE_MOBILE)="
2718                                             + conn.isNetworkSupported(TYPE_MOBILE)
2719                     + " isReady=" + isReady);
2720         }
2721         return retVal;
2722     }
2723
2724     /*
2725      * TODO: consider adding to TelephonyManager or SubscritpionManager.
2726      */
2727     public static boolean hasReadyMobileRadio(Context context, int subId) {
2728         if (TEST_RADIOS) {
2729             return SystemProperties.get(TEST_RADIOS_PROP).contains("mobile");
2730         }
2731
2732         final ConnectivityManager conn = ConnectivityManager.from(context);
2733         final TelephonyManager tele = TelephonyManager.from(context);
2734         final int slotId = SubscriptionManager.getSlotId(subId);
2735         final boolean isReady = tele.getSimState(slotId) == SIM_STATE_READY;
2736
2737         boolean retVal =  conn.isNetworkSupported(TYPE_MOBILE) && isReady;
2738         if (LOGD) Log.d(TAG, "hasReadyMobileRadio: subId=" + subId
2739                 + " conn.isNetworkSupported(TYPE_MOBILE)=" + conn.isNetworkSupported(TYPE_MOBILE)
2740                 + " isReady=" + isReady);
2741         return retVal;
2742     }
2743
2744     /**
2745      * Test if device has a mobile 4G data radio.
2746      */
2747     public static boolean hasReadyMobile4gRadio(Context context) {
2748         if (!NetworkPolicyEditor.ENABLE_SPLIT_POLICIES) {
2749             return false;
2750         }
2751         if (TEST_RADIOS) {
2752             return SystemProperties.get(TEST_RADIOS_PROP).contains("4g");
2753         }
2754
2755         final ConnectivityManager conn = ConnectivityManager.from(context);
2756         final TelephonyManager tele = TelephonyManager.from(context);
2757
2758         final boolean hasWimax = conn.isNetworkSupported(TYPE_WIMAX);
2759         final boolean hasLte = (tele.getLteOnCdmaMode() == PhoneConstants.LTE_ON_CDMA_TRUE)
2760                 && hasReadyMobileRadio(context);
2761         return hasWimax || hasLte;
2762     }
2763
2764     /**
2765      * Test if device has a Wi-Fi data radio.
2766      */
2767     public static boolean hasWifiRadio(Context context) {
2768         if (TEST_RADIOS) {
2769             return SystemProperties.get(TEST_RADIOS_PROP).contains("wifi");
2770         }
2771
2772         final ConnectivityManager conn = ConnectivityManager.from(context);
2773         return conn.isNetworkSupported(TYPE_WIFI);
2774     }
2775
2776     /**
2777      * Test if device has an ethernet network connection.
2778      */
2779     public boolean hasEthernet(Context context) {
2780         if (TEST_RADIOS) {
2781             return SystemProperties.get(TEST_RADIOS_PROP).contains("ethernet");
2782         }
2783
2784         final ConnectivityManager conn = ConnectivityManager.from(context);
2785         final boolean hasEthernet = conn.isNetworkSupported(TYPE_ETHERNET);
2786
2787         final long ethernetBytes;
2788         if (mStatsSession != null) {
2789             try {
2790                 ethernetBytes = mStatsSession.getSummaryForNetwork(
2791                         NetworkTemplate.buildTemplateEthernet(), Long.MIN_VALUE, Long.MAX_VALUE)
2792                         .getTotalBytes();
2793             } catch (RemoteException e) {
2794                 throw new RuntimeException(e);
2795             }
2796         } else {
2797             ethernetBytes = 0;
2798         }
2799
2800         // only show ethernet when both hardware present and traffic has occurred
2801         return hasEthernet && ethernetBytes > 0;
2802     }
2803
2804     /**
2805      * Inflate a {@link Preference} style layout, adding the given {@link View}
2806      * widget into {@link android.R.id#widget_frame}.
2807      */
2808     private static View inflatePreference(LayoutInflater inflater, ViewGroup root, View widget) {
2809         final View view = inflater.inflate(R.layout.preference, root, false);
2810         final LinearLayout widgetFrame = (LinearLayout) view.findViewById(
2811                 android.R.id.widget_frame);
2812         widgetFrame.addView(widget, new LinearLayout.LayoutParams(WRAP_CONTENT, WRAP_CONTENT));
2813         return view;
2814     }
2815
2816     private static View inflatePreferenceWithInvisibleWidget(LayoutInflater inflater,
2817             ViewGroup root, View widget) {
2818         final ViewGroup view = (ViewGroup) inflater.inflate(R.layout.preference, root, false);
2819         view.addView(widget, 0);
2820         final ViewGroup.LayoutParams lp = widget.getLayoutParams();
2821         lp.width = 0;
2822         widget.setLayoutParams(lp);
2823         return view;
2824     }
2825
2826     /**
2827      * Test if any networks are currently limited.
2828      */
2829     private boolean hasLimitedNetworks() {
2830         return !buildLimitedNetworksList().isEmpty();
2831     }
2832
2833     /**
2834      * Build string describing currently limited networks, which defines when
2835      * background data is restricted.
2836      */
2837     @Deprecated
2838     private CharSequence buildLimitedNetworksString() {
2839         final List<CharSequence> limited = buildLimitedNetworksList();
2840
2841         // handle case where no networks limited
2842         if (limited.isEmpty()) {
2843             limited.add(getText(R.string.data_usage_list_none));
2844         }
2845
2846         return TextUtils.join(limited);
2847     }
2848
2849     /**
2850      * Build list of currently limited networks, which defines when background
2851      * data is restricted.
2852      */
2853     @Deprecated
2854     private List<CharSequence> buildLimitedNetworksList() {
2855         final Context context = getActivity();
2856
2857         // build combined list of all limited networks
2858         final ArrayList<CharSequence> limited = Lists.newArrayList();
2859
2860         final TelephonyManager tele = TelephonyManager.from(context);
2861         if (tele.getSimState() == SIM_STATE_READY) {
2862             final String subscriberId = getActiveSubscriberId(context);
2863             if (mPolicyEditor.hasLimitedPolicy(buildTemplateMobileAll(subscriberId))) {
2864                 limited.add(getText(R.string.data_usage_list_mobile));
2865             }
2866             if (mPolicyEditor.hasLimitedPolicy(buildTemplateMobile3gLower(subscriberId))) {
2867                 limited.add(getText(R.string.data_usage_tab_3g));
2868             }
2869             if (mPolicyEditor.hasLimitedPolicy(buildTemplateMobile4g(subscriberId))) {
2870                 limited.add(getText(R.string.data_usage_tab_4g));
2871             }
2872         }
2873
2874         if (mPolicyEditor.hasLimitedPolicy(buildTemplateWifiWildcard())) {
2875             limited.add(getText(R.string.data_usage_tab_wifi));
2876         }
2877         if (mPolicyEditor.hasLimitedPolicy(buildTemplateEthernet())) {
2878             limited.add(getText(R.string.data_usage_tab_ethernet));
2879         }
2880
2881         return limited;
2882     }
2883
2884     /**
2885      * Inset both selector and divider {@link Drawable} on the given
2886      * {@link ListView} by the requested dimensions.
2887      */
2888     private static void insetListViewDrawables(ListView view, int insetSide) {
2889         final Drawable selector = view.getSelector();
2890         final Drawable divider = view.getDivider();
2891
2892         // fully unregister these drawables so callbacks can be maintained after
2893         // wrapping below.
2894         final Drawable stub = new ColorDrawable(Color.TRANSPARENT);
2895         view.setSelector(stub);
2896         view.setDivider(stub);
2897
2898         view.setSelector(new InsetBoundsDrawable(selector, insetSide));
2899         view.setDivider(new InsetBoundsDrawable(divider, insetSide));
2900     }
2901
2902     /**
2903      * Set {@link android.R.id#title} for a preference view inflated with
2904      * {@link #inflatePreference(LayoutInflater, ViewGroup, View)}.
2905      */
2906     private static void setPreferenceTitle(View parent, int resId) {
2907         final TextView title = (TextView) parent.findViewById(android.R.id.title);
2908         title.setText(resId);
2909     }
2910
2911     /**
2912      * Set {@link android.R.id#summary} for a preference view inflated with
2913      * {@link #inflatePreference(LayoutInflater, ViewGroup, View)}.
2914      */
2915     private static void setPreferenceSummary(View parent, CharSequence string) {
2916         final TextView summary = (TextView) parent.findViewById(android.R.id.summary);
2917         summary.setVisibility(View.VISIBLE);
2918         summary.setText(string);
2919     }
2920
2921     /**
2922      * For search
2923      */
2924     public static final SearchIndexProvider SEARCH_INDEX_DATA_PROVIDER =
2925         new BaseSearchIndexProvider() {
2926             @Override
2927             public List<SearchIndexableRaw> getRawDataToIndex(Context context, boolean enabled) {
2928                 final List<SearchIndexableRaw> result = new ArrayList<SearchIndexableRaw>();
2929
2930                 final Resources res = context.getResources();
2931
2932                 // Add fragment title
2933                 SearchIndexableRaw data = new SearchIndexableRaw(context);
2934                 data.title = res.getString(R.string.data_usage_summary_title);
2935                 data.screenTitle = res.getString(R.string.data_usage_summary_title);
2936                 result.add(data);
2937
2938                 // Mobile data
2939                 data = new SearchIndexableRaw(context);
2940                 data.key = DATA_USAGE_ENABLE_MOBILE_KEY;
2941                 data.title = res.getString(R.string.data_usage_enable_mobile);
2942                 data.screenTitle = res.getString(R.string.data_usage_summary_title);
2943                 result.add(data);
2944
2945                 // Set mobile data limit
2946                 data = new SearchIndexableRaw(context);
2947                 data.key = DATA_USAGE_DISABLE_MOBILE_LIMIT_KEY;
2948                 data.title = res.getString(R.string.data_usage_disable_mobile_limit);
2949                 data.screenTitle = res.getString(R.string.data_usage_summary_title);
2950                 result.add(data);
2951
2952                 // Data usage cycle
2953                 data = new SearchIndexableRaw(context);
2954                 data.key = DATA_USAGE_CYCLE_KEY;
2955                 data.title = res.getString(R.string.data_usage_cycle);
2956                 data.screenTitle = res.getString(R.string.data_usage_summary_title);
2957                 result.add(data);
2958
2959                 return result;
2960             }
2961         };
2962
2963         private void addMobileTab(Context context, SubscriptionInfo subInfo, boolean isMultiSim) {
2964             if (subInfo != null && mMobileTagMap != null) {
2965                 if (hasReadyMobileRadio(context, subInfo.getSubscriptionId())) {
2966                     if (isMultiSim) {
2967                         mTabHost.addTab(buildTabSpec(mMobileTagMap.get(subInfo.getSubscriptionId()),
2968                                 subInfo.getDisplayName()));
2969                     } else {
2970                         mTabHost.addTab(buildTabSpec(mMobileTagMap.get(subInfo.getSubscriptionId()),
2971                                 R.string.data_usage_tab_mobile));
2972                     }
2973                 }
2974             } else {
2975                 if (LOGD) Log.d(TAG, "addMobileTab: subInfoList is null");
2976             }
2977         }
2978
2979         private SubscriptionInfo getCurrentTabSubInfo(Context context) {
2980             if (mSubInfoList != null && mTabHost != null) {
2981                 final int currentTagIndex = mTabHost.getCurrentTab();
2982                 int i = 0;
2983                 for (SubscriptionInfo subInfo : mSubInfoList) {
2984                     if (hasReadyMobileRadio(context, subInfo.getSubscriptionId())) {
2985                         if (i++ == currentTagIndex) {
2986                             return subInfo;
2987                         }
2988                     }
2989                 }
2990             }
2991             return null;
2992         }
2993
2994         /**
2995          * Init a map with subId key and mobile tag name
2996          * @param subInfoList The subscription Info List
2997          * @return The map or null if no activated subscription
2998          */
2999         private Map<Integer, String> initMobileTabTag(List<SubscriptionInfo> subInfoList) {
3000             Map<Integer, String> map = null;
3001             if (subInfoList != null) {
3002                 String mobileTag;
3003                 map = new HashMap<Integer, String>();
3004                 for (SubscriptionInfo subInfo : subInfoList) {
3005                     mobileTag = TAB_MOBILE + String.valueOf(subInfo.getSubscriptionId());
3006                     map.put(subInfo.getSubscriptionId(), mobileTag);
3007                 }
3008             }
3009             return map;
3010         }
3011
3012         private static boolean isMobileTab(String currentTab) {
3013             return currentTab != null ? currentTab.contains(TAB_MOBILE) : false;
3014         }
3015
3016         private int getSubId(String currentTab) {
3017             if (mMobileTagMap != null) {
3018                 Set<Integer> set = mMobileTagMap.keySet();
3019                 for (Integer subId : set) {
3020                     if (mMobileTagMap.get(subId).equals(currentTab)) {
3021                         return subId;
3022                     }
3023                 }
3024             }
3025             Log.e(TAG, "currentTab = " + currentTab + " non mobile tab called this function");
3026             return -1;
3027         }
3028
3029         private boolean isMobileDataAvailable(int subId) {
3030             return mSubscriptionManager.getActiveSubscriptionInfo(subId) != null;
3031         }
3032 }