2 * Copyright (C) 2011 The Android Open Source Project
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
8 * http://www.apache.org/licenses/LICENSE-2.0
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.
17 package com.android.settings;
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;
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;
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;
150 import libcore.util.Objects;
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;
161 * Panel showing data usage history across various networks, including options
162 * to inspect based on usage cycle and control through {@link NetworkPolicy}.
164 public class DataUsageSummary extends HighlightingFragment implements Indexable {
165 private static final String TAG = "DataUsage";
166 private static final boolean LOGD = false;
168 // TODO: remove this testing code
169 private static final boolean TEST_ANIM = false;
170 private static final boolean TEST_RADIOS = false;
172 private static final String TEST_RADIOS_PROP = "test.radios";
173 private static final String TEST_SUBSCRIBER_PROP = "test.subscriberid";
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";
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";
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";
198 public static final String EXTRA_SHOW_APP_IMMEDIATE_PKG = "showAppImmediatePkg";
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;
206 private INetworkManagementService mNetworkService;
207 private INetworkStatsService mStatsService;
208 private NetworkPolicyManager mPolicyManager;
209 private TelephonyManager mTelephonyManager;
210 private SubscriptionManager mSubscriptionManager;
212 private INetworkStatsSession mStatsSession;
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";
219 private SharedPreferences mPrefs;
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;
229 /** Distance to inset content from sides, when needed. */
230 private int mInsetSide = 0;
232 private ViewGroup mHeader;
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;
243 private View mCycleView;
244 private Spinner mCycleSpinner;
245 private CycleAdapter mCycleAdapter;
246 private TextView mCycleSummary;
248 private ChartDataUsageView mChart;
249 private View mDisclaimer;
250 private TextView mEmpty;
251 private View mStupidPadding;
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;
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;
269 private boolean mShowWifi = false;
270 private boolean mShowEthernet = false;
271 private boolean mShowAlerts = false;
272 private boolean mDataAlertsSupported = false;
274 private NetworkTemplate mTemplate;
275 private ChartData mChartData;
277 private AppItem mCurrentApp = null;
279 private Intent mAppSettingsIntent;
281 private NetworkPolicyEditor mPolicyEditor;
283 private String mCurrentTab = null;
284 private String mIntentTab = null;
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;
294 private List<SubscriptionInfo> mSubInfoList;
295 private Map<Integer,String> mMobileTagMap;
297 /** Flag used to ignore listeners during binding. */
298 private boolean mBinding;
300 private UidDetailProvider mUidDetailProvider;
302 // Indicates request to show app immediately rather than list.
303 private String mShowAppImmediatePkg;
306 * Local cache of data enabled for subId, used to work around delays.
308 private final Map<String, Boolean> mMobileDataEnabled = new HashMap<String, Boolean>();
311 protected int getMetricsCategory() {
312 return MetricsLogger.DATA_USAGE_SUMMARY;
316 public void onCreate(Bundle savedInstanceState) {
317 super.onCreate(savedInstanceState);
318 final Context context = getActivity();
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);
328 mPrefs = getActivity().getSharedPreferences(PREF_FILE, Context.MODE_PRIVATE);
330 mPolicyEditor = new NetworkPolicyEditor(mPolicyManager);
331 mPolicyEditor.read();
333 mSubInfoList = mSubscriptionManager.getActiveSubscriptionInfoList();
334 mMobileTagMap = initMobileTabTag(mSubInfoList);
337 if (!mNetworkService.isBandwidthControlEnabled()) {
338 Log.w(TAG, "No bandwidth control; leaving");
339 getActivity().finish();
341 } catch (RemoteException e) {
342 Log.w(TAG, "No bandwidth control; leaving");
343 getActivity().finish();
347 mStatsSession = mStatsService.openSession();
348 } catch (RemoteException e) {
349 throw new RuntimeException(e);
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);
356 // override preferences when no mobile radio
357 if (!hasReadyMobileRadio(context)) {
359 mShowEthernet = true;
362 mUidDetailProvider = new UidDetailProvider(context);
364 Bundle arguments = getArguments();
365 if (arguments != null) {
366 mShowAppImmediatePkg = arguments.getString(EXTRA_SHOW_APP_IMMEDIATE_PKG);
369 setHasOptionsMenu(true);
373 public View onCreateView(LayoutInflater inflater, ViewGroup container,
374 Bundle savedInstanceState) {
376 final Context context = inflater.getContext();
377 final View view = inflater.inflate(R.layout.data_usage_summary, container, false);
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);
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;
391 // adjust padding around tabwidget as needed
392 prepareCustomPreferencesList(container, view, mListView, false);
395 mTabHost.setOnTabChangedListener(mTabListener);
397 mHeader = (ViewGroup) inflater.inflate(R.layout.data_usage_header, mListView, false);
398 mHeader.setClickable(true);
400 mListView.addHeaderView(new View(context), null, true);
401 mListView.addHeaderView(mHeader, null, true);
402 mListView.setItemsCanFocus(true);
404 if (mInsetSide > 0) {
405 // inset selector and divider drawables
406 insetListViewDrawables(mListView, mInsetSide);
407 mHeader.setPaddingRelative(mInsetSide, 0, mInsetSide, 0);
411 // bind network switches
412 mNetworkSwitchesContainer = (ViewGroup) mHeader.findViewById(
413 R.id.network_switches_container);
414 mNetworkSwitches = (LinearLayout) mHeader.findViewById(R.id.network_switches);
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);
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);
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);
450 mChart = (ChartDataUsageView) mHeader.findViewById(R.id.chart);
451 mChart.setListener(mChartListener);
452 mChart.bindNetworkPolicy(null);
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);
463 mAppSettings = (Button) mAppDetail.findViewById(R.id.app_settings);
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));
469 mRestrictSpinner = new Spinner(inflater.getContext());
470 mRestrictSpinner.setAdapter(restrictAdapter);
471 mRestrictSpinner.setOnItemSelectedListener(mAppRestrictListener);
473 mAppRestrictView = inflatePreferenceWithInvisibleWidget(inflater,
474 mAppSwitches, mRestrictSpinner);
475 mAppRestrictView.setClickable(true);
476 mAppRestrictView.setFocusable(true);
477 mAppRestrictView.setOnClickListener(new View.OnClickListener() {
479 public void onClick(View v) {
480 mRestrictSpinner.performClick();
483 mAppSwitches.addView(mAppRestrictView);
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);
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);
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);
512 mDisclaimer = mHeader.findViewById(R.id.disclaimer);
513 mEmpty = (TextView) mHeader.findViewById(android.R.id.empty);
514 mStupidPadding = mHeader.findViewById(R.id.stupid_padding);
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);
521 showRequestedAppIfNeeded(view);
526 private void showRequestedAppIfNeeded(View rootView) {
527 if (mShowAppImmediatePkg == null) {
531 int uid = getActivity().getPackageManager().getPackageUid(mShowAppImmediatePkg,
532 UserHandle.myUserId());
533 AppItem app = new AppItem(uid);
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)
546 getActivity().finish();
551 public void onViewStateRestored(Bundle savedInstanceState) {
552 super.onViewStateRestored(savedInstanceState);
554 // pick default tab based on incoming intent
555 final Intent intent = getActivity().getIntent();
556 mIntentTab = computeTabFromIntent(intent);
558 // this kicks off chain reaction which creates tabs, binds the body to
559 // selected network, and binds chart, cycles and detail list.
564 public void onResume() {
567 getView().post(new Runnable() {
570 highlightViewIfNeeded();
574 // kick off background task to update stats
575 new AsyncTask<Void, Void, Void>() {
577 protected Void doInBackground(Void... params) {
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) {
589 protected void onPostExecute(Void result) {
594 }.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
598 public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
599 inflater.inflate(R.menu.data_usage, menu);
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;
608 mMenuShowWifi = menu.findItem(R.id.data_usage_menu_show_wifi);
609 if (hasWifiRadio(context) && hasReadyMobileRadio(context)) {
610 mMenuShowWifi.setVisible(!appDetailMode);
612 mMenuShowWifi.setVisible(false);
615 mMenuShowEthernet = menu.findItem(R.id.data_usage_menu_show_ethernet);
616 if (hasEthernet(context) && hasReadyMobileRadio(context)) {
617 mMenuShowEthernet.setVisible(!appDetailMode);
619 mMenuShowEthernet.setVisible(false);
622 mMenuRestrictBackground = menu.findItem(R.id.data_usage_menu_restrict_background);
623 mMenuRestrictBackground.setVisible(
624 hasReadyMobileRadio(context) && isOwner && !appDetailMode);
626 final MenuItem metered = menu.findItem(R.id.data_usage_menu_metered);
627 if (hasReadyMobileRadio(context) || hasWifiRadio(context)) {
628 metered.setVisible(!appDetailMode);
630 metered.setVisible(false);
633 // TODO: show when multiple sims available
634 mMenuSimCards = menu.findItem(R.id.data_usage_menu_sim_cards);
635 mMenuSimCards.setVisible(false);
637 mMenuCellularNetworks = menu.findItem(R.id.data_usage_menu_cellular_networks);
638 mMenuCellularNetworks.setVisible(hasReadyMobileRadio(context)
639 && !appDetailMode && isOwner);
641 final MenuItem help = menu.findItem(R.id.data_usage_menu_help);
643 if (!TextUtils.isEmpty(helpUrl = getResources().getString(R.string.help_url_data_usage))) {
644 HelpUtils.prepareHelpMenuItem(getActivity(), help, helpUrl, getClass().getName());
646 help.setVisible(false);
649 mMenuDataAlerts = menu.findItem(R.id.data_usage_menu_data_alerts);
651 if (mDataAlertsSupported) {
652 mMenuDataAlerts.setVisible(!appDetailMode);
654 mMenuDataAlerts.setVisible(false);
657 mMenuResetStats = menu.findItem(R.id.data_usage_menu_reset_stats);
658 mMenuResetStats.setVisible(!appDetailMode);
663 private void updateMenuTitles() {
664 if (mPolicyManager.getRestrictBackground()) {
665 mMenuRestrictBackground.setTitle(R.string.data_usage_menu_allow_background);
667 mMenuRestrictBackground.setTitle(R.string.data_usage_menu_restrict_background);
671 mMenuShowWifi.setTitle(R.string.data_usage_menu_hide_wifi);
673 mMenuShowWifi.setTitle(R.string.data_usage_menu_show_wifi);
677 mMenuShowEthernet.setTitle(R.string.data_usage_menu_hide_ethernet);
679 mMenuShowEthernet.setTitle(R.string.data_usage_menu_show_ethernet);
682 mMenuDataAlerts.setTitle(R.string.data_usage_menu_disable_data_alerts);
684 mMenuDataAlerts.setTitle(R.string.data_usage_menu_enable_data_alerts);
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);
696 // no confirmation to drop restriction
697 setRestrictBackground(false);
701 case R.id.data_usage_menu_show_wifi: {
702 mShowWifi = !mShowWifi;
703 mPrefs.edit().putBoolean(PREF_SHOW_WIFI, mShowWifi).apply();
708 case R.id.data_usage_menu_show_ethernet: {
709 mShowEthernet = !mShowEthernet;
710 mPrefs.edit().putBoolean(PREF_SHOW_ETHERNET, mShowEthernet).apply();
715 case R.id.data_usage_menu_sim_cards: {
716 // TODO: hook up to sim cards
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);
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);
732 case R.id.data_usage_menu_reset_stats: {
733 ConfirmDataResetFragment.show(DataUsageSummary.this, mTemplate);
736 case R.id.data_usage_menu_data_alerts: {
737 updateShowAlertsState(!mShowAlerts);
745 public void onDestroy() {
746 mDataEnabledView = null;
747 mDisableAtLimitView = null;
749 mUidDetailProvider.clearCache();
750 mUidDetailProvider = null;
752 TrafficStats.closeQuietly(mStatsSession);
757 private void updateShowAlertsState(boolean showAlert) {
758 mShowAlerts = showAlert;
759 mPrefs.edit().putBoolean(PREF_ENABLE_DATA_USAGE_NOTIFY, mShowAlerts).apply();
761 DataUsageUtils.enableDataUsageService(getContext(), mShowAlerts);
765 * Build and assign {@link LayoutTransition} to various containers. Should
766 * only be assigned after initial layout is complete.
768 private void ensureLayoutTransitions() {
769 if (mShowAppImmediatePkg != null) {
770 // If we are skipping right to showing an app, we don't care about transitions.
773 // skip when already setup
774 if (mChart.getLayoutTransition() != null) return;
776 mTabsContainer.setLayoutTransition(buildLayoutTransition());
777 mHeader.setLayoutTransition(buildLayoutTransition());
778 mNetworkSwitchesContainer.setLayoutTransition(buildLayoutTransition());
780 final LayoutTransition chartTransition = buildLayoutTransition();
781 chartTransition.disableTransitionType(LayoutTransition.APPEARING);
782 chartTransition.disableTransitionType(LayoutTransition.DISAPPEARING);
783 mChart.setLayoutTransition(chartTransition);
786 private static LayoutTransition buildLayoutTransition() {
787 final LayoutTransition transition = new LayoutTransition();
789 transition.setDuration(1500);
791 transition.setAnimateParentHierarchy(false);
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.
800 private void updateTabs() {
801 final Context context = getActivity();
802 mTabHost.clearAllTabs();
804 int simCount = mTelephonyManager.getSimCount();
806 List<SubscriptionInfo> sirs = mSubscriptionManager.getActiveSubscriptionInfoList();
808 for (SubscriptionInfo sir : sirs) {
809 addMobileTab(context, sir, (simCount > 1));
813 if (mShowWifi && hasWifiRadio(context)) {
814 mTabHost.addTab(buildTabSpec(TAB_WIFI, R.string.data_usage_tab_wifi));
817 if (mShowEthernet && hasEthernet(context)) {
818 mTabHost.addTab(buildTabSpec(TAB_ETHERNET, R.string.data_usage_tab_ethernet));
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);
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
836 mTabHost.setCurrentTabByTag(mIntentTab);
840 // no usable tabs, so hide body
843 // already hit updateBody() when added; ignore
848 * Factory that provide empty {@link View} to make {@link TabHost} happy.
850 private TabContentFactory mEmptyTabContent = new TabContentFactory() {
852 public View createTabContent(String tag) {
853 return new View(mTabHost.getContext());
858 * Build {@link TabSpec} with thin indicator, and empty content.
860 private TabSpec buildTabSpec(String tag, int titleRes) {
861 return mTabHost.newTabSpec(tag).setIndicator(getText(titleRes)).setContent(
866 * Build {@link TabSpec} with thin indicator, and empty content.
868 private TabSpec buildTabSpec(String tag, CharSequence title) {
869 return mTabHost.newTabSpec(tag).setIndicator(title).setContent(
874 private OnTabChangeListener mTabListener = new OnTabChangeListener() {
876 public void onTabChanged(String tabId) {
877 // user changed tab; update body
883 * Update body content based on current tab. Loads
884 * {@link NetworkStatsHistory} and {@link NetworkPolicy} from system, and
885 * binds them to visible controls.
887 private void updateBody() {
889 if (!isAdded()) return;
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;
896 if (currentTab == null) {
897 Log.w(TAG, "no tab selected; hiding body");
898 mListView.setVisibility(View.GONE);
901 mListView.setVisibility(View.VISIBLE);
904 mCurrentTab = currentTab;
906 if (LOGD) Log.d(TAG, "updateBody() with currentTab=" + currentTab);
908 mDataEnabledSupported = isOwner;
909 mDisableAtLimitSupported = true;
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));
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));
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());
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));
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));
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();
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();
957 if (LOGD) Log.d(TAG, "updateBody() unknown tab");
958 throw new IllegalStateException("unknown tab: " + currentTab);
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;
970 totalBytes = mStatsService.getNetworkTotalBytes(policy.template, start, end);
971 } catch (RuntimeException e) {
972 } catch (RemoteException e) {
975 if (policy.isOverLimit(totalBytes) && policy.lastLimitSnooze < start) {
976 setPreferenceSummary(mDataEnabledView,
977 getString(R.string.data_usage_cellular_data_summary));
979 final TextView summary = (TextView) mDataEnabledView
980 .findViewById(android.R.id.summary);
981 summary.setVisibility(View.GONE);
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);
991 // detail mode can change visible menus, invalidate
992 getActivity().invalidateOptionsMenu();
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);
1004 seriesColor = sir.getIconTint();
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);
1014 private boolean isAppDetailMode() {
1015 return mCurrentApp != null;
1019 * Update UID details panels to match {@link #mCurrentApp}, showing or
1020 * hiding them depending on {@link #isAppDetailMode()}.
1022 private void updateAppDetail() {
1023 final Context context = getActivity();
1024 final PackageManager pm = context.getPackageManager();
1025 final LayoutInflater inflater = getActivity().getLayoutInflater();
1027 if (isAppDetailMode()) {
1028 mAppDetail.setVisibility(View.VISIBLE);
1029 mCycleAdapter.setChangeVisible(false);
1031 mAppDetail.setVisibility(View.GONE);
1032 mCycleAdapter.setChangeVisible(true);
1034 // hide detail stats when not in detail mode
1035 mChart.bindDetailNetworkStats(null);
1039 // remove warning/limit sweeps while in detail mode
1040 mChart.bindNetworkPolicy(null);
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);
1047 mAppTitles.removeAllViews();
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);
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);
1069 // Remember last slot for summary
1070 if (title != null) {
1071 mAppTotal = (TextView) title.findViewById(R.id.app_summary);
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);
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) {
1092 mAppSettings.setOnClickListener(new OnClickListener() {
1094 public void onClick(View v) {
1099 // TODO: target towards entire UID instead of just first package
1100 getActivity().startActivityAsUser(mAppSettingsIntent,
1101 new UserHandle(UserHandle.getUserId(uid)));
1104 mAppSettings.setEnabled(matchFound);
1105 mAppSettings.setVisibility(View.VISIBLE);
1108 mAppSettingsIntent = null;
1109 mAppSettings.setOnClickListener(null);
1110 mAppSettings.setVisibility(View.GONE);
1115 if (UserHandle.isApp(uid) && isBandwidthControlEnabled()) {
1116 setPreferenceTitle(mAppRestrictView, R.string.background_data_access);
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;
1125 summaryResId = R.string.allow_background_wlan;
1126 position = DATA_USAGE_BACKGROUND_WLAN_ACCESS;
1129 summaryResId = R.string.allow_background_both;
1130 position = DATA_USAGE_BACKGROUND_FULL_ACCESS;
1132 setPreferenceSummary(mAppRestrictView, getString(summaryResId));
1133 mRestrictSpinner.setSelection(position);
1134 mAppRestrictView.setVisibility(View.VISIBLE);
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());
1144 mAppCellularAccessView.setVisibility(View.GONE);
1147 if (mDataAlertsSupported) {
1148 setPreferenceTitle(mAppDataAlertView, R.string.mobile_data_alert);
1149 setPreferenceSummary(mAppDataAlertView,
1150 getString(R.string.mobile_data_alert_summary));
1152 mAppDataAlertView.setVisibility(View.VISIBLE);
1153 mAppDataAlert.setChecked(getAppDataAlert());
1155 mAppDataAlertView.setVisibility(View.GONE);
1158 mAppRestrictView.setVisibility(View.GONE);
1159 mAppDataAlertView.setVisibility(View.GONE);
1160 mAppCellularAccessView.setVisibility(View.GONE);
1164 private void setPolicyWarningBytes(long warningBytes) {
1165 if (LOGD) Log.d(TAG, "setPolicyWarningBytes()");
1166 mPolicyEditor.setPolicyWarningBytes(mTemplate, warningBytes);
1167 updatePolicy(false);
1170 private void setPolicyLimitBytes(long limitBytes) {
1171 if (LOGD) Log.d(TAG, "setPolicyLimitBytes()");
1172 mPolicyEditor.setPolicyLimitBytes(mTemplate, limitBytes);
1173 updatePolicy(false);
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();
1184 Log.d(TAG, "isMobileDataEnabled: != null, subId=" + subId
1185 + " isEnable=" + isEnable);
1187 mMobileDataEnabled.put(String.valueOf(subId), null);
1190 isEnable = mTelephonyManager.getDataEnabled(subId);
1192 Log.d(TAG, "isMobileDataEnabled: == null, subId=" + subId
1193 + " isEnable=" + isEnable);
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);
1207 private void resetDataStats(NetworkTemplate template) {
1208 // kick off background task to reset stats
1209 new AsyncTask<Void, Void, Void>() {
1211 protected Void doInBackground(Void... params) {
1213 mStatsService.resetDataUsageHistoryForAllUid(mTemplate);
1214 mPolicyEditor.setPolicyLimitBytes(mTemplate,
1215 mPolicyEditor.getPolicyLimitBytes(mTemplate));
1216 mStatsService.forceUpdate();
1217 } catch (RemoteException e) {
1223 protected void onPostExecute (Void result) {
1227 }.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
1230 private boolean isNetworkPolicyModifiable(NetworkPolicy policy) {
1231 return policy != null && isBandwidthControlEnabled() && mDataEnabled.isChecked()
1232 && ActivityManager.getCurrentUser() == UserHandle.USER_OWNER;
1235 private boolean isBandwidthControlEnabled() {
1237 return mNetworkService.isBandwidthControlEnabled();
1238 } catch (RemoteException e) {
1239 Log.w(TAG, "problem talking with INetworkManagementService: " + e);
1244 public void setRestrictBackground(boolean restrictBackground) {
1245 mPolicyManager.setRestrictBackground(restrictBackground);
1249 private int getAppRestrictBackground() {
1250 final int uid = mCurrentApp.key;
1251 final int uidPolicy = mPolicyManager.getUidPolicy(uid);
1253 return ((uidPolicy & POLICY_REJECT_METERED_BACKGROUND) | (uidPolicy &
1254 POLICY_REJECT_ON_WLAN_BACKGROUND));
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);
1262 if (newPolicy == currentPolicy) {
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);
1271 mPolicyManager.removeUidPolicy(uid, POLICY_REJECT_METERED_BACKGROUND);
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);
1280 mPolicyManager.removeUidPolicy(uid, POLICY_REJECT_ON_WLAN_BACKGROUND);
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;
1289 summaryResId = R.string.allow_background_wlan;
1292 summaryResId = R.string.allow_background_both;
1294 setPreferenceSummary(mAppRestrictView, getString(summaryResId));
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;
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);
1309 mPolicyManager.removeUidPolicy(uid, POLICY_REJECT_ON_DATA);
1311 mAppCellularAccess.setChecked(restrictCellular);
1315 private void setAppDataAlert(boolean enableDataAlert) {
1316 final int uid = mCurrentApp.key;
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() : "";
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");
1331 mAppDataAlert.setChecked(enableDataAlert);
1333 // automatically enable global alert for notifications when enabling first per app alert
1334 if (enableDataAlert && !mShowAlerts) {
1335 updateShowAlertsState(true);
1339 private boolean getAppDataAlert() {
1340 final int uid = mCurrentApp.key;
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");
1352 * Update chart sweeps and cycle list to reflect {@link NetworkPolicy} for
1353 * current {@link #mTemplate}.
1355 private void updatePolicy(boolean refreshCycle) {
1356 boolean dataEnabledVisible = mDataEnabledSupported;
1357 boolean disableAtLimitVisible = mDisableAtLimitSupported;
1359 if (isAppDetailMode()) {
1360 dataEnabledVisible = false;
1361 disableAtLimitVisible = false;
1364 // TODO: move enabled state directly into policy
1365 if (isMobileTab(mCurrentTab)) {
1367 mDataEnabled.setChecked(isMobileDataEnabled(getSubId(mCurrentTab)));
1371 final NetworkPolicy policy = mPolicyEditor.getPolicy(mTemplate);
1373 if (isNetworkPolicyModifiable(policy) && isMobileDataAvailable(getSubId(mCurrentTab))) {
1374 mDisableAtLimit.setChecked(policy != null && policy.limitBytes != LIMIT_DISABLED);
1375 if (!isAppDetailMode()) {
1376 mChart.bindNetworkPolicy(policy);
1380 // controls are disabled; don't bind warning/limit sweeps
1381 disableAtLimitVisible = false;
1382 mChart.bindNetworkPolicy(null);
1385 mDataEnabledView.setVisibility(dataEnabledVisible ? View.VISIBLE : View.GONE);
1386 mDisableAtLimitView.setVisibility(disableAtLimitVisible ? View.VISIBLE : View.GONE);
1389 // generate cycle list based on policy and available history
1390 updateCycleList(policy);
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}.
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();
1404 final Context context = mCycleSpinner.getContext();
1405 NetworkStatsHistory.Entry entry = null;
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();
1414 final long now = System.currentTimeMillis();
1415 if (historyStart == Long.MAX_VALUE) historyStart = now;
1416 if (historyEnd == Long.MIN_VALUE) historyEnd = now + 1;
1418 boolean hasCycles = false;
1419 if (policy != null) {
1420 // find the next cycle boundary
1421 long cycleEnd = computeNextCycleBoundary(historyEnd, policy);
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="
1429 final boolean includeCycle;
1430 if (mChartData != null) {
1431 entry = mChartData.network.getValues(cycleStart, cycleEnd, entry);
1432 includeCycle = (entry.rxBytes + entry.txBytes) > 0;
1434 includeCycle = true;
1438 mCycleAdapter.add(new CycleItem(context, cycleStart, cycleEnd));
1441 cycleEnd = cycleStart;
1444 // one last cycle entry to modify policy cycle day
1445 mCycleAdapter.setChangePossible(isNetworkPolicyModifiable(policy));
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);
1454 final boolean includeCycle;
1455 if (mChartData != null) {
1456 entry = mChartData.network.getValues(cycleStart, cycleEnd, entry);
1457 includeCycle = (entry.rxBytes + entry.txBytes) > 0;
1459 includeCycle = true;
1463 mCycleAdapter.add(new CycleItem(context, cycleStart, cycleEnd));
1465 cycleEnd = cycleStart;
1468 mCycleAdapter.setChangePossible(false);
1471 // force pick the current cycle (first item)
1472 if (mCycleAdapter.getCount() > 0) {
1473 final int position = mCycleAdapter.findNearestPosition(previousItem);
1474 mCycleSpinner.setSelection(position);
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);
1482 // but still kick off loader for detailed list
1490 private View.OnClickListener mDataEnabledListener = new View.OnClickListener() {
1492 public void onClick(View v) {
1493 if (mBinding) return;
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);
1500 setMobileDataEnabled(getSubId(currentTab), true);
1502 // disabling data; show confirmation dialog which eventually
1503 // calls setMobileDataEnabled() once user confirms.
1504 ConfirmDataDisableFragment.show(DataUsageSummary.this, getSubId(mCurrentTab));
1508 updatePolicy(false);
1512 private View.OnClickListener mDisableAtLimitListener = new View.OnClickListener() {
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);
1521 setPolicyLimitBytes(LIMIT_DISABLED);
1526 private AdapterView.OnItemSelectedListener mAppRestrictListener =
1527 new AdapterView.OnItemSelectedListener() {
1529 public void onItemSelected(AdapterView<?> parent, View v, int position, long id) {
1530 final int backgroundPolicy;
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;
1538 backgroundPolicy = DATA_USAGE_BACKGROUND_FULL_ACCESS;
1540 setAppRestrictBackground(backgroundPolicy);
1544 public void onNothingSelected(AdapterView<?> parent) {
1549 private View.OnClickListener mAppRestrictCellularListener = new View.OnClickListener() {
1551 public void onClick(View v) {
1552 final boolean restrictCellular = !mAppCellularAccess.isChecked();
1554 if (restrictCellular) {
1555 // enabling restriction; show confirmation dialog which
1556 // eventually calls setRestrictCellular() once user
1558 ConfirmAppRestrictCellularFragment.show(DataUsageSummary.this);
1560 setAppRestrictCellular(false);
1565 private View.OnClickListener mAppDataAlertListner = new View.OnClickListener() {
1567 public void onClick(View v) {
1568 final boolean enableDataAlert = !mAppDataAlert.isChecked();
1569 setAppDataAlert(enableDataAlert);
1573 private OnItemClickListener mListListener = new OnItemClickListener() {
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);
1579 // TODO: sigh, remove this hack once we understand 6450986
1580 if (mUidDetailProvider == null || app == null) return;
1582 final UidDetail detail = mUidDetailProvider.getUidDetail(app.key, true);
1583 AppDetailsFragment.show(DataUsageSummary.this, app, detail.label);
1587 private OnItemSelectedListener mCycleListener = new OnItemSelectedListener() {
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);
1596 // reset spinner to something other than "change cycle..."
1597 mCycleSpinner.setSelection(0);
1601 Log.d(TAG, "showing cycle " + cycle + ", start=" + cycle.start + ", end="
1605 // update chart to show selected cycle, and update detail data
1606 // to match updated sweep bounds.
1607 mChart.setVisibleRange(cycle.start, cycle.end);
1614 public void onNothingSelected(AdapterView<?> parent) {
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
1625 private void updateDetailData() {
1626 if (LOGD) Log.d(TAG, "updateDetailData()");
1628 final long start = mChart.getInspectStart();
1629 final long end = mChart.getInspectEnd();
1630 final long now = System.currentTimeMillis();
1632 final Context context = getActivity();
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;
1643 if (mAppTotal != null) {
1644 mAppTotal.setText(Formatter.formatFileSize(context, totalBytes));
1646 mAppBackground.setText(Formatter.formatFileSize(context, defaultBytes));
1647 mAppForeground.setText(Formatter.formatFileSize(context, foregroundBytes));
1649 // and finally leave with summary data for label below
1650 entry = mChartData.detail.getValues(start, end, now, null);
1652 getLoaderManager().destroyLoader(LOADER_SUMMARY);
1654 mCycleSummary.setVisibility(View.GONE);
1657 if (mChartData != null) {
1658 entry = mChartData.network.getValues(start, end, now, null);
1661 mCycleSummary.setVisibility(View.VISIBLE);
1663 // kick off loader for detailed stats
1664 getLoaderManager().restartLoader(LOADER_SUMMARY,
1665 SummaryForAllUidLoader.buildArgs(mTemplate, start, end), mSummaryCallbacks);
1668 final long totalBytes = entry != null ? entry.rxBytes + entry.txBytes : 0;
1669 final String totalPhrase = Formatter.formatFileSize(context, totalBytes);
1670 mCycleSummary.setText(totalPhrase);
1672 if (isMobileTab(mCurrentTab) || TAB_3G.equals(mCurrentTab)
1673 || TAB_4G.equals(mCurrentTab)) {
1674 if (isAppDetailMode()) {
1675 mDisclaimer.setVisibility(View.GONE);
1677 mDisclaimer.setVisibility(View.VISIBLE);
1680 mDisclaimer.setVisibility(View.GONE);
1683 // initial layout is finished above, ensure we have transitions
1684 ensureLayoutTransitions();
1687 private final LoaderCallbacks<ChartData> mChartDataCallbacks = new LoaderCallbacks<
1690 public Loader<ChartData> onCreateLoader(int id, Bundle args) {
1691 return new ChartDataLoader(getActivity(), mStatsSession, args);
1695 public void onLoadFinished(Loader<ChartData> loader, ChartData data) {
1697 mChart.bindNetworkStats(mChartData.network);
1698 mChart.bindDetailNetworkStats(mChartData.detail);
1700 // calcuate policy cycles based on available data
1704 // force scroll to top of body when showing detail
1705 if (mChartData.detail != null) {
1706 mListView.smoothScrollToPosition(0);
1711 public void onLoaderReset(Loader<ChartData> loader) {
1713 mChart.bindNetworkStats(null);
1714 mChart.bindDetailNetworkStats(null);
1718 private final LoaderCallbacks<NetworkStats> mSummaryCallbacks = new LoaderCallbacks<
1721 public Loader<NetworkStats> onCreateLoader(int id, Bundle args) {
1722 return new SummaryForAllUidLoader(getActivity(), mStatsSession, args);
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();
1734 public void onLoaderReset(Loader<NetworkStats> loader) {
1735 mAdapter.bindStats(null, new int[0]);
1736 updateEmptyVisible();
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);
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);
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);
1761 private DataUsageChartListener mChartListener = new DataUsageChartListener() {
1763 public void onWarningChanged() {
1764 setPolicyWarningBytes(mChart.getWarningBytes());
1768 public void onLimitChanged() {
1769 setPolicyLimitBytes(mChart.getLimitBytes());
1774 public void requestWarningEdit() {
1775 WarningEditorFragment.show(DataUsageSummary.this);
1779 public void requestLimitEdit() {
1780 LimitEditorFragment.show(DataUsageSummary.this);
1785 * List item that reflects a specific data usage cycle.
1787 public static class CycleItem implements Comparable<CycleItem> {
1788 public CharSequence label;
1792 CycleItem(CharSequence label) {
1796 public CycleItem(Context context, long start, long end) {
1797 this.label = formatDateRange(context, start, end);
1803 public String toString() {
1804 return label.toString();
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;
1817 public int compareTo(CycleItem another) {
1818 return Long.compare(start, another.start);
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());
1826 public static String formatDateRange(Context context, long start, long end) {
1827 final int flags = FORMAT_SHOW_DATE | FORMAT_ABBREV_MONTH;
1829 synchronized (sBuilder) {
1830 sBuilder.setLength(0);
1831 return DateUtils.formatDateRange(context, sFormatter, start, end, flags, null)
1837 * Special-case data usage cycle that triggers dialog to change
1838 * {@link NetworkPolicy#cycleDay}.
1840 public static class CycleChangeItem extends CycleItem {
1841 public CycleChangeItem(Context context) {
1842 super(context.getString(R.string.data_usage_change_cycle));
1846 public static class CycleAdapter extends ArrayAdapter<CycleItem> {
1847 private boolean mChangePossible = false;
1848 private boolean mChangeVisible = false;
1850 private final CycleChangeItem mChangeItem;
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);
1858 public void setChangePossible(boolean possible) {
1859 mChangePossible = possible;
1863 public void setChangeVisible(boolean visible) {
1864 mChangeVisible = visible;
1868 private void updateChange() {
1869 remove(mChangeItem);
1870 if (mChangePossible && mChangeVisible) {
1876 * Find position of {@link CycleItem} in this adapter which is nearest
1877 * the given {@link CycleItem}.
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) {
1886 } else if (item.compareTo(target) >= 0) {
1896 * Adapter of applications, sorted by total usage descending.
1898 public static class DataUsageAdapter extends BaseAdapter {
1899 private final UidDetailProvider mProvider;
1900 private final int mInsetSide;
1901 private final UserManager mUm;
1903 private ArrayList<AppItem> mItems = Lists.newArrayList();
1904 private long mLargest;
1906 public DataUsageAdapter(final UserManager userManager, UidDetailProvider provider, int insetSide) {
1907 mProvider = checkNotNull(provider);
1908 mInsetSide = insetSide;
1913 * Bind the given {@link NetworkStats}, or {@code null} to clear list.
1915 public void bindStats(NetworkStats stats, int[] restrictedUids) {
1919 final int currentUserId = ActivityManager.getCurrentUser();
1920 final List<UserHandle> profiles = mUm.getUserProfiles();
1921 final SparseArray<AppItem> knownItems = new SparseArray<AppItem>();
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);
1928 // Decide how to collapse items together
1929 final int uid = entry.uid;
1931 final int collapseKey;
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);
1944 category = AppItem.CATEGORY_APP;
1946 // If it is a removed user add it to the removed users' key
1947 final UserInfo info = mUm.getUserInfo(userId);
1949 collapseKey = UID_REMOVED;
1950 category = AppItem.CATEGORY_APP;
1952 // Add to other user item.
1953 collapseKey = UidDetailProvider.buildKeyForUser(userId);
1954 category = AppItem.CATEGORY_USER;
1957 } else if (uid == UID_REMOVED || uid == UID_TETHERING) {
1959 category = AppItem.CATEGORY_APP;
1961 collapseKey = android.os.Process.SYSTEM_UID;
1962 category = AppItem.CATEGORY_APP;
1964 accumulate(collapseKey, knownItems, entry, category);
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)))) {
1975 AppItem item = knownItems.get(uid);
1977 item = new AppItem(uid);
1980 knownItems.put(item.key, item);
1982 item.restricted = true;
1985 if (!mItems.isEmpty()) {
1986 final AppItem title = new AppItem();
1987 title.category = AppItem.CATEGORY_APP_TITLE;
1991 Collections.sort(mItems);
1992 notifyDataSetChanged();
1996 * Accumulate data usage of a network stats entry for the item mapped by the collapse key.
1997 * Creates the item if needed.
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
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);
2010 item = new AppItem(collapseKey);
2011 item.category = itemCategory;
2013 knownItems.put(item.key, item);
2016 item.total += entry.rxBytes + entry.txBytes;
2017 if (mLargest < item.total) {
2018 mLargest = item.total;
2023 public int getCount() {
2024 return mItems.size();
2028 public Object getItem(int position) {
2029 return mItems.get(position);
2033 public long getItemId(int position) {
2034 return mItems.get(position).key;
2038 * See {@link #getItemViewType} for the view types.
2041 public int getViewTypeCount() {
2046 * Returns 1 for separator items and 0 for anything else.
2049 public int getItemViewType(int position) {
2050 final AppItem item = mItems.get(position);
2051 if (item.category == AppItem.CATEGORY_APP_TITLE) {
2059 public boolean areAllItemsEnabled() {
2064 public boolean isEnabled(int position) {
2065 if (position > mItems.size()) {
2066 throw new ArrayIndexOutOfBoundsException();
2068 return getItemViewType(position) == 0;
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);
2080 final TextView title = (TextView) convertView.findViewById(android.R.id.title);
2081 title.setText(R.string.data_usage_app);
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));
2089 if (mInsetSide > 0) {
2090 convertView.setPaddingRelative(mInsetSide, 0, mInsetSide, 0);
2094 final Context context = parent.getContext();
2096 final TextView summary = (TextView) convertView.findViewById(android.R.id.summary);
2097 final ProgressBar progress = (ProgressBar) convertView.findViewById(
2098 android.R.id.progress);
2100 // kick off async load of app details
2101 UidDetailTask.bindView(mProvider, item, convertView);
2103 if (item.restricted && item.total <= 0) {
2104 summary.setText(R.string.data_usage_app_restricted);
2105 progress.setVisibility(View.GONE);
2107 summary.setText(Formatter.formatFileSize(context, item.total));
2108 progress.setVisibility(View.VISIBLE);
2111 final int percentTotal = mLargest != 0 ? (int) (item.total * 100 / mLargest) : 0;
2112 progress.setProgress(percentTotal);
2120 * Empty {@link Fragment} that controls display of UID details in
2121 * {@link DataUsageSummary}.
2123 public static class AppDetailsFragment extends Fragment {
2124 private static final String EXTRA_APP = "app";
2126 public static void show(DataUsageSummary parent, AppItem app, CharSequence label) {
2127 show(parent, app, label, true);
2130 public static void show(DataUsageSummary parent, AppItem app, CharSequence label,
2131 boolean addToBack) {
2132 if (!parent.isAdded()) return;
2134 final Bundle args = new Bundle();
2135 args.putParcelable(EXTRA_APP, app);
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);
2143 ft.addToBackStack(TAG_APP_DETAILS);
2145 ft.setBreadCrumbTitle(
2146 parent.getResources().getString(R.string.data_usage_app_summary_title));
2147 ft.commitAllowingStateLoss();
2151 public void onStart() {
2153 final DataUsageSummary target = (DataUsageSummary) getTargetFragment();
2154 target.mCurrentApp = getArguments().getParcelable(EXTRA_APP);
2155 target.updateBody();
2159 public void onStop() {
2161 final DataUsageSummary target = (DataUsageSummary) getTargetFragment();
2162 target.mCurrentApp = null;
2163 target.updateBody();
2168 * Dialog to request user confirmation before setting
2169 * {@link NetworkPolicy#limitBytes}.
2171 public static class ConfirmLimitFragment extends DialogFragment {
2172 private static final String EXTRA_MESSAGE = "message";
2173 private static final String EXTRA_LIMIT_BYTES = "limitBytes";
2175 public static void show(DataUsageSummary parent) {
2176 if (!parent.isAdded()) return;
2178 final NetworkPolicy policy = parent.mPolicyEditor.getPolicy(parent.mTemplate);
2179 if (policy == null) return;
2181 final Resources res = parent.getResources();
2182 final CharSequence message;
2183 final long minLimitBytes = (long) (policy.warningBytes * 1.2f);
2184 final long limitBytes;
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);
2198 throw new IllegalArgumentException("unknown current tab: " + currentTab);
2201 final Bundle args = new Bundle();
2202 args.putCharSequence(EXTRA_MESSAGE, message);
2203 args.putLong(EXTRA_LIMIT_BYTES, limitBytes);
2205 final ConfirmLimitFragment dialog = new ConfirmLimitFragment();
2206 dialog.setArguments(args);
2207 dialog.setTargetFragment(parent, 0);
2208 dialog.show(parent.getFragmentManager(), TAG_CONFIRM_LIMIT);
2212 public Dialog onCreateDialog(Bundle savedInstanceState) {
2213 final Context context = getActivity();
2215 final CharSequence message = getArguments().getCharSequence(EXTRA_MESSAGE);
2216 final long limitBytes = getArguments().getLong(EXTRA_LIMIT_BYTES);
2218 final AlertDialog.Builder builder = new AlertDialog.Builder(context);
2219 builder.setTitle(R.string.data_usage_limit_dialog_title);
2220 builder.setMessage(message);
2222 builder.setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() {
2224 public void onClick(DialogInterface dialog, int which) {
2225 final DataUsageSummary target = (DataUsageSummary) getTargetFragment();
2226 if (target != null) {
2227 target.setPolicyLimitBytes(limitBytes);
2232 return builder.create();
2237 * Dialog to edit {@link NetworkPolicy#cycleDay}.
2239 public static class CycleEditorFragment extends DialogFragment {
2240 private static final String EXTRA_TEMPLATE = "template";
2242 public static void show(DataUsageSummary parent) {
2243 if (!parent.isAdded()) return;
2245 final Bundle args = new Bundle();
2246 args.putParcelable(EXTRA_TEMPLATE, parent.mTemplate);
2248 final CycleEditorFragment dialog = new CycleEditorFragment();
2249 dialog.setArguments(args);
2250 dialog.setTargetFragment(parent, 0);
2251 dialog.show(parent.getFragmentManager(), TAG_CYCLE_EDITOR);
2255 public Dialog onCreateDialog(Bundle savedInstanceState) {
2256 final Context context = getActivity();
2257 final DataUsageSummary target = (DataUsageSummary) getTargetFragment();
2258 final NetworkPolicyEditor editor = target.mPolicyEditor;
2260 final AlertDialog.Builder builder = new AlertDialog.Builder(context);
2261 final LayoutInflater dialogInflater = LayoutInflater.from(builder.getContext());
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);
2266 final NetworkTemplate template = getArguments().getParcelable(EXTRA_TEMPLATE);
2267 final int cycleDay = editor.getPolicyCycleDay(template);
2269 cycleDayPicker.setMinValue(1);
2270 cycleDayPicker.setMaxValue(31);
2271 cycleDayPicker.setValue(cycleDay);
2272 cycleDayPicker.setWrapSelectorWheel(true);
2274 builder.setTitle(R.string.data_usage_cycle_editor_title);
2275 builder.setView(view);
2277 builder.setPositiveButton(R.string.data_usage_cycle_editor_positive,
2278 new DialogInterface.OnClickListener() {
2280 public void onClick(DialogInterface dialog, int which) {
2281 // clear focus to finish pending text edits
2282 cycleDayPicker.clearFocus();
2284 final int cycleDay = cycleDayPicker.getValue();
2285 final String cycleTimezone = new Time().timezone;
2286 editor.setPolicyCycleDay(template, cycleDay, cycleTimezone);
2287 target.updatePolicy(true);
2291 return builder.create();
2296 * Dialog to edit {@link NetworkPolicy#warningBytes}.
2298 public static class WarningEditorFragment extends DialogFragment {
2299 private static final String EXTRA_TEMPLATE = "template";
2301 public static void show(DataUsageSummary parent) {
2302 if (!parent.isAdded()) return;
2304 final Bundle args = new Bundle();
2305 args.putParcelable(EXTRA_TEMPLATE, parent.mTemplate);
2307 final WarningEditorFragment dialog = new WarningEditorFragment();
2308 dialog.setArguments(args);
2309 dialog.setTargetFragment(parent, 0);
2310 dialog.show(parent.getFragmentManager(), TAG_WARNING_EDITOR);
2314 public Dialog onCreateDialog(Bundle savedInstanceState) {
2315 final Context context = getActivity();
2316 final DataUsageSummary target = (DataUsageSummary) getTargetFragment();
2317 final NetworkPolicyEditor editor = target.mPolicyEditor;
2319 final AlertDialog.Builder builder = new AlertDialog.Builder(context);
2320 final LayoutInflater dialogInflater = LayoutInflater.from(builder.getContext());
2322 final View view = dialogInflater.inflate(R.layout.data_usage_bytes_editor, null, false);
2323 final NumberPicker bytesPicker = (NumberPicker) view.findViewById(R.id.bytes);
2325 final NetworkTemplate template = getArguments().getParcelable(EXTRA_TEMPLATE);
2326 final long warningBytes = editor.getPolicyWarningBytes(template);
2327 final long limitBytes = editor.getPolicyLimitBytes(template);
2329 bytesPicker.setMinValue(0);
2330 if (limitBytes != LIMIT_DISABLED) {
2331 bytesPicker.setMaxValue((int) (limitBytes / MB_IN_BYTES) - 1);
2333 bytesPicker.setMaxValue(Integer.MAX_VALUE);
2335 bytesPicker.setValue((int) (warningBytes / MB_IN_BYTES));
2336 bytesPicker.setWrapSelectorWheel(false);
2338 builder.setTitle(R.string.data_usage_warning_editor_title);
2339 builder.setView(view);
2341 builder.setPositiveButton(R.string.data_usage_cycle_editor_positive,
2342 new DialogInterface.OnClickListener() {
2344 public void onClick(DialogInterface dialog, int which) {
2345 // clear focus to finish pending text edits
2346 bytesPicker.clearFocus();
2348 final long bytes = bytesPicker.getValue() * MB_IN_BYTES;
2349 editor.setPolicyWarningBytes(template, bytes);
2350 target.updatePolicy(false);
2354 return builder.create();
2359 * Dialog to edit {@link NetworkPolicy#limitBytes}.
2361 public static class LimitEditorFragment extends DialogFragment {
2362 private static final String EXTRA_TEMPLATE = "template";
2364 public static void show(DataUsageSummary parent) {
2365 if (!parent.isAdded()) return;
2367 final Bundle args = new Bundle();
2368 args.putParcelable(EXTRA_TEMPLATE, parent.mTemplate);
2370 final LimitEditorFragment dialog = new LimitEditorFragment();
2371 dialog.setArguments(args);
2372 dialog.setTargetFragment(parent, 0);
2373 dialog.show(parent.getFragmentManager(), TAG_LIMIT_EDITOR);
2377 public Dialog onCreateDialog(Bundle savedInstanceState) {
2378 final Context context = getActivity();
2379 final DataUsageSummary target = (DataUsageSummary) getTargetFragment();
2380 final NetworkPolicyEditor editor = target.mPolicyEditor;
2382 final AlertDialog.Builder builder = new AlertDialog.Builder(context);
2383 final LayoutInflater dialogInflater = LayoutInflater.from(builder.getContext());
2385 final View view = dialogInflater.inflate(R.layout.data_usage_bytes_editor, null, false);
2386 final NumberPicker bytesPicker = (NumberPicker) view.findViewById(R.id.bytes);
2388 final NetworkTemplate template = getArguments().getParcelable(EXTRA_TEMPLATE);
2389 final long warningBytes = editor.getPolicyWarningBytes(template);
2390 final long limitBytes = editor.getPolicyLimitBytes(template);
2392 bytesPicker.setMaxValue(Integer.MAX_VALUE);
2393 if (warningBytes != WARNING_DISABLED && limitBytes > 0) {
2394 bytesPicker.setMinValue((int) (warningBytes / MB_IN_BYTES) + 1);
2396 bytesPicker.setMinValue(0);
2398 bytesPicker.setValue((int) (limitBytes / MB_IN_BYTES));
2399 bytesPicker.setWrapSelectorWheel(false);
2401 builder.setTitle(R.string.data_usage_limit_editor_title);
2402 builder.setView(view);
2404 builder.setPositiveButton(R.string.data_usage_cycle_editor_positive,
2405 new DialogInterface.OnClickListener() {
2407 public void onClick(DialogInterface dialog, int which) {
2408 // clear focus to finish pending text edits
2409 bytesPicker.clearFocus();
2411 final long bytes = bytesPicker.getValue() * MB_IN_BYTES;
2412 editor.setPolicyLimitBytes(template, bytes);
2413 target.updatePolicy(false);
2417 return builder.create();
2421 * Dialog to request user confirmation before disabling data.
2423 public static class ConfirmDataDisableFragment extends DialogFragment {
2425 public static void show(DataUsageSummary parent, int subId) {
2427 if (!parent.isAdded()) return;
2429 final ConfirmDataDisableFragment dialog = new ConfirmDataDisableFragment();
2430 dialog.setTargetFragment(parent, 0);
2431 dialog.show(parent.getFragmentManager(), TAG_CONFIRM_DATA_DISABLE);
2435 public Dialog onCreateDialog(Bundle savedInstanceState) {
2436 final Context context = getActivity();
2438 final AlertDialog.Builder builder = new AlertDialog.Builder(context);
2439 builder.setMessage(R.string.data_usage_disable_mobile);
2441 builder.setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() {
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);
2451 builder.setNegativeButton(android.R.string.cancel, null);
2453 return builder.create();
2458 * Dialog to request user confirmation before resetting data.
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;
2466 final ConfirmDataResetFragment dialog = new ConfirmDataResetFragment();
2467 dialog.setTargetFragment(parent, 0);
2468 dialog.show(parent.getFragmentManager(), TAG_CONFIRM_DATA_RESET);
2472 public Dialog onCreateDialog(Bundle savedInstanceState) {
2473 final Context context = getActivity();
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);
2479 builder.setPositiveButton(R.string.reset_stats_confirm,
2480 new DialogInterface.OnClickListener() {
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);
2490 builder.setNegativeButton(android.R.string.cancel, null);
2492 return builder.create();
2497 * Dialog to request user confirmation before setting
2498 * {@link INetworkPolicyManager#setRestrictBackground(boolean)}.
2500 public static class ConfirmRestrictFragment extends DialogFragment {
2501 public static void show(DataUsageSummary parent) {
2502 if (!parent.isAdded()) return;
2504 final ConfirmRestrictFragment dialog = new ConfirmRestrictFragment();
2505 dialog.setTargetFragment(parent, 0);
2506 dialog.show(parent.getFragmentManager(), TAG_CONFIRM_RESTRICT);
2510 public Dialog onCreateDialog(Bundle savedInstanceState) {
2511 final Context context = getActivity();
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);
2518 builder.setMessage(R.string.data_usage_restrict_background);
2521 builder.setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() {
2523 public void onClick(DialogInterface dialog, int which) {
2524 final DataUsageSummary target = (DataUsageSummary) getTargetFragment();
2525 if (target != null) {
2526 target.setRestrictBackground(true);
2530 builder.setNegativeButton(android.R.string.cancel, null);
2532 return builder.create();
2537 * Dialog to inform user that {@link #POLICY_REJECT_METERED_BACKGROUND}
2538 * change has been denied, usually based on
2539 * {@link DataUsageSummary#hasLimitedNetworks()}.
2541 public static class DeniedRestrictFragment extends DialogFragment {
2542 public static void show(DataUsageSummary parent) {
2543 if (!parent.isAdded()) return;
2545 final DeniedRestrictFragment dialog = new DeniedRestrictFragment();
2546 dialog.setTargetFragment(parent, 0);
2547 dialog.show(parent.getFragmentManager(), TAG_DENIED_RESTRICT);
2551 public Dialog onCreateDialog(Bundle savedInstanceState) {
2552 final Context context = getActivity();
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);
2559 return builder.create();
2564 * Dialog to request user confirmation before setting
2565 * {@link #POLICY_REJECT_ON_DATA}.
2567 public static class ConfirmAppRestrictCellularFragment extends DialogFragment {
2568 public static void show(DataUsageSummary parent) {
2569 if (!parent.isAdded()) return;
2571 final ConfirmAppRestrictCellularFragment dialog = new
2572 ConfirmAppRestrictCellularFragment();
2573 dialog.setTargetFragment(parent, 0);
2574 dialog.show(parent.getFragmentManager(), TAG_CONFIRM_APP_RESTRICT_CELLULAR);
2578 public Dialog onCreateDialog(Bundle savedInstanceState) {
2579 final Context context = getActivity();
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);
2585 builder.setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() {
2587 public void onClick(DialogInterface dialog, int which) {
2588 final DataUsageSummary target = (DataUsageSummary) getTargetFragment();
2589 if (target != null) {
2590 target.setAppRestrictCellular(true);
2594 builder.setNegativeButton(android.R.string.cancel, null);
2596 return builder.create();
2601 * Compute default tab that should be selected, based on
2602 * {@link NetworkPolicyManager#EXTRA_NETWORK_TEMPLATE} extra.
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);
2615 switch (template.getMatchRule()) {
2616 case MATCH_MOBILE_3G_LOWER:
2618 case MATCH_MOBILE_4G:
2620 case MATCH_MOBILE_ALL:
2630 * Background task that loads {@link UidDetail}, binding to
2631 * {@link DataUsageAdapter} row item when finished.
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;
2638 private UidDetailTask(UidDetailProvider provider, AppItem item, View target) {
2639 mProvider = checkNotNull(provider);
2640 mItem = checkNotNull(item);
2641 mTarget = checkNotNull(target);
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);
2651 final UidDetail cachedDetail = provider.getUidDetail(item.key, false);
2652 if (cachedDetail != null) {
2653 bindView(cachedDetail, target);
2655 target.setTag(new UidDetailTask(provider, item, target).executeOnExecutor(
2656 AsyncTask.THREAD_POOL_EXECUTOR));
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);
2664 if (detail != null) {
2665 icon.setImageDrawable(detail.icon);
2666 title.setText(detail.label);
2667 title.setContentDescription(detail.contentDescription);
2669 icon.setImageDrawable(null);
2670 title.setText(null);
2675 protected void onPreExecute() {
2676 bindView(null, mTarget);
2680 protected UidDetail doInBackground(Void... params) {
2681 return mProvider.getUidDetail(mItem.key, true);
2685 protected void onPostExecute(UidDetail result) {
2686 bindView(result, mTarget);
2691 * Test if device has a mobile data radio with SIM in ready state.
2693 public static boolean hasReadyMobileRadio(Context context) {
2695 return SystemProperties.get(TEST_RADIOS_PROP).contains("mobile");
2698 final ConnectivityManager conn = ConnectivityManager.from(context);
2699 final TelephonyManager tele = TelephonyManager.from(context);
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");
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);
2714 boolean retVal = conn.isNetworkSupported(TYPE_MOBILE) && isReady;
2716 Log.d(TAG, "hasReadyMobileRadio:"
2717 + " conn.isNetworkSupported(TYPE_MOBILE)="
2718 + conn.isNetworkSupported(TYPE_MOBILE)
2719 + " isReady=" + isReady);
2725 * TODO: consider adding to TelephonyManager or SubscritpionManager.
2727 public static boolean hasReadyMobileRadio(Context context, int subId) {
2729 return SystemProperties.get(TEST_RADIOS_PROP).contains("mobile");
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;
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);
2745 * Test if device has a mobile 4G data radio.
2747 public static boolean hasReadyMobile4gRadio(Context context) {
2748 if (!NetworkPolicyEditor.ENABLE_SPLIT_POLICIES) {
2752 return SystemProperties.get(TEST_RADIOS_PROP).contains("4g");
2755 final ConnectivityManager conn = ConnectivityManager.from(context);
2756 final TelephonyManager tele = TelephonyManager.from(context);
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;
2765 * Test if device has a Wi-Fi data radio.
2767 public static boolean hasWifiRadio(Context context) {
2769 return SystemProperties.get(TEST_RADIOS_PROP).contains("wifi");
2772 final ConnectivityManager conn = ConnectivityManager.from(context);
2773 return conn.isNetworkSupported(TYPE_WIFI);
2777 * Test if device has an ethernet network connection.
2779 public boolean hasEthernet(Context context) {
2781 return SystemProperties.get(TEST_RADIOS_PROP).contains("ethernet");
2784 final ConnectivityManager conn = ConnectivityManager.from(context);
2785 final boolean hasEthernet = conn.isNetworkSupported(TYPE_ETHERNET);
2787 final long ethernetBytes;
2788 if (mStatsSession != null) {
2790 ethernetBytes = mStatsSession.getSummaryForNetwork(
2791 NetworkTemplate.buildTemplateEthernet(), Long.MIN_VALUE, Long.MAX_VALUE)
2793 } catch (RemoteException e) {
2794 throw new RuntimeException(e);
2800 // only show ethernet when both hardware present and traffic has occurred
2801 return hasEthernet && ethernetBytes > 0;
2805 * Inflate a {@link Preference} style layout, adding the given {@link View}
2806 * widget into {@link android.R.id#widget_frame}.
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));
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();
2822 widget.setLayoutParams(lp);
2827 * Test if any networks are currently limited.
2829 private boolean hasLimitedNetworks() {
2830 return !buildLimitedNetworksList().isEmpty();
2834 * Build string describing currently limited networks, which defines when
2835 * background data is restricted.
2838 private CharSequence buildLimitedNetworksString() {
2839 final List<CharSequence> limited = buildLimitedNetworksList();
2841 // handle case where no networks limited
2842 if (limited.isEmpty()) {
2843 limited.add(getText(R.string.data_usage_list_none));
2846 return TextUtils.join(limited);
2850 * Build list of currently limited networks, which defines when background
2851 * data is restricted.
2854 private List<CharSequence> buildLimitedNetworksList() {
2855 final Context context = getActivity();
2857 // build combined list of all limited networks
2858 final ArrayList<CharSequence> limited = Lists.newArrayList();
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));
2866 if (mPolicyEditor.hasLimitedPolicy(buildTemplateMobile3gLower(subscriberId))) {
2867 limited.add(getText(R.string.data_usage_tab_3g));
2869 if (mPolicyEditor.hasLimitedPolicy(buildTemplateMobile4g(subscriberId))) {
2870 limited.add(getText(R.string.data_usage_tab_4g));
2874 if (mPolicyEditor.hasLimitedPolicy(buildTemplateWifiWildcard())) {
2875 limited.add(getText(R.string.data_usage_tab_wifi));
2877 if (mPolicyEditor.hasLimitedPolicy(buildTemplateEthernet())) {
2878 limited.add(getText(R.string.data_usage_tab_ethernet));
2885 * Inset both selector and divider {@link Drawable} on the given
2886 * {@link ListView} by the requested dimensions.
2888 private static void insetListViewDrawables(ListView view, int insetSide) {
2889 final Drawable selector = view.getSelector();
2890 final Drawable divider = view.getDivider();
2892 // fully unregister these drawables so callbacks can be maintained after
2894 final Drawable stub = new ColorDrawable(Color.TRANSPARENT);
2895 view.setSelector(stub);
2896 view.setDivider(stub);
2898 view.setSelector(new InsetBoundsDrawable(selector, insetSide));
2899 view.setDivider(new InsetBoundsDrawable(divider, insetSide));
2903 * Set {@link android.R.id#title} for a preference view inflated with
2904 * {@link #inflatePreference(LayoutInflater, ViewGroup, View)}.
2906 private static void setPreferenceTitle(View parent, int resId) {
2907 final TextView title = (TextView) parent.findViewById(android.R.id.title);
2908 title.setText(resId);
2912 * Set {@link android.R.id#summary} for a preference view inflated with
2913 * {@link #inflatePreference(LayoutInflater, ViewGroup, View)}.
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);
2924 public static final SearchIndexProvider SEARCH_INDEX_DATA_PROVIDER =
2925 new BaseSearchIndexProvider() {
2927 public List<SearchIndexableRaw> getRawDataToIndex(Context context, boolean enabled) {
2928 final List<SearchIndexableRaw> result = new ArrayList<SearchIndexableRaw>();
2930 final Resources res = context.getResources();
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);
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);
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);
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);
2963 private void addMobileTab(Context context, SubscriptionInfo subInfo, boolean isMultiSim) {
2964 if (subInfo != null && mMobileTagMap != null) {
2965 if (hasReadyMobileRadio(context, subInfo.getSubscriptionId())) {
2967 mTabHost.addTab(buildTabSpec(mMobileTagMap.get(subInfo.getSubscriptionId()),
2968 subInfo.getDisplayName()));
2970 mTabHost.addTab(buildTabSpec(mMobileTagMap.get(subInfo.getSubscriptionId()),
2971 R.string.data_usage_tab_mobile));
2975 if (LOGD) Log.d(TAG, "addMobileTab: subInfoList is null");
2979 private SubscriptionInfo getCurrentTabSubInfo(Context context) {
2980 if (mSubInfoList != null && mTabHost != null) {
2981 final int currentTagIndex = mTabHost.getCurrentTab();
2983 for (SubscriptionInfo subInfo : mSubInfoList) {
2984 if (hasReadyMobileRadio(context, subInfo.getSubscriptionId())) {
2985 if (i++ == currentTagIndex) {
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
2999 private Map<Integer, String> initMobileTabTag(List<SubscriptionInfo> subInfoList) {
3000 Map<Integer, String> map = null;
3001 if (subInfoList != null) {
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);
3012 private static boolean isMobileTab(String currentTab) {
3013 return currentTab != null ? currentTab.contains(TAB_MOBILE) : false;
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)) {
3025 Log.e(TAG, "currentTab = " + currentTab + " non mobile tab called this function");
3029 private boolean isMobileDataAvailable(int subId) {
3030 return mSubscriptionManager.getActiveSubscriptionInfo(subId) != null;