Merge "Controls to set expensive (metered) networks."

This commit is contained in:
Jeff Sharkey
2012-04-05 16:18:00 -07:00
committed by Android (Google) Code Review
6 changed files with 336 additions and 113 deletions

View File

@@ -35,4 +35,7 @@
android:id="@+id/data_usage_menu_show_ethernet" android:id="@+id/data_usage_menu_show_ethernet"
android:title="@string/data_usage_menu_show_ethernet" android:title="@string/data_usage_menu_show_ethernet"
android:checkable="true" /> android:checkable="true" />
<item
android:id="@+id/data_usage_menu_metered"
android:title="@string/data_usage_menu_metered" />
</menu> </menu>

View File

@@ -3609,6 +3609,8 @@
<string name="data_usage_menu_show_wifi">Show Wi-Fi usage</string> <string name="data_usage_menu_show_wifi">Show Wi-Fi usage</string>
<!-- Title for checkbox menu option to show Ethernet data usage. [CHAR LIMIT=32] --> <!-- Title for checkbox menu option to show Ethernet data usage. [CHAR LIMIT=32] -->
<string name="data_usage_menu_show_ethernet">Show Ethernet usage</string> <string name="data_usage_menu_show_ethernet">Show Ethernet usage</string>
<!-- Title for menu option to configure expensive networks. [CHAR LIMIT=32] -->
<string name="data_usage_menu_metered">Expensive networks</string>
<!-- Title for option to change data usage cycle day. [CHAR LIMIT=32] --> <!-- Title for option to change data usage cycle day. [CHAR LIMIT=32] -->
<string name="data_usage_change_cycle">Change cycle\u2026</string> <string name="data_usage_change_cycle">Change cycle\u2026</string>
<!-- Body of dialog prompting user to change numerical day of month that data usage cycle should reset. [CHAR LIMIT=64] --> <!-- Body of dialog prompting user to change numerical day of month that data usage cycle should reset. [CHAR LIMIT=64] -->
@@ -3710,6 +3712,15 @@
<!-- Label displaying total network data transferred during a specific time period. [CHAR LIMIT=64] --> <!-- Label displaying total network data transferred during a specific time period. [CHAR LIMIT=64] -->
<string name="data_usage_total_during_range"><xliff:g id="range" example="Jul 1 - Jul 31">%2$s</xliff:g>: about <xliff:g id="total" example="128KB">%1$s</xliff:g> used</string> <string name="data_usage_total_during_range"><xliff:g id="range" example="Jul 1 - Jul 31">%2$s</xliff:g>: about <xliff:g id="total" example="128KB">%1$s</xliff:g> used</string>
<!-- Dialog title for selecting paid networks. [CHAR LIMIT=25] -->
<string name="data_usage_metered_title">Expensive networks</string>
<!-- Dialog body for selecting paid networks. [CHAR LIMIT=NONE] -->
<string name="data_usage_metered_body">Select the networks for which data usage is expensive. Apps can be restricted from using these networks when in the background. Apps may also warn before using these networks for large downloads.</string>
<!-- Header for list of mobile networks. [CHAR LIMIT=32] -->
<string name="data_usage_metered_mobile">Mobile networks</string>
<!-- Header for list of Wi-Fi networks. [CHAR LIMIT=32] -->
<string name="data_usage_metered_wifi">Wi-Fi networks</string>
<!-- Button at the bottom of the CryptKeeper screen to make an emergency call. --> <!-- Button at the bottom of the CryptKeeper screen to make an emergency call. -->
<string name="cryptkeeper_emergency_call">Emergency call</string> <string name="cryptkeeper_emergency_call">Emergency call</string>
<!-- Button at the bottom of the CryptKeeper screen that lets the user return to a call --> <!-- Button at the bottom of the CryptKeeper screen that lets the user return to a call -->

View File

@@ -0,0 +1,34 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Copyright (C) 2012 The Android Open Source Project
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android">
<Preference
android:summary="@string/data_usage_metered_body"
android:persistent="false"
android:selectable="false" />
<PreferenceCategory
android:key="mobile"
android:title="@string/data_usage_metered_mobile"
android:persistent="false" />
<PreferenceCategory
android:key="wifi"
android:title="@string/data_usage_metered_wifi"
android:persistent="false" />
</PreferenceScreen>

View File

@@ -35,11 +35,12 @@ import static android.net.NetworkTemplate.buildTemplateEthernet;
import static android.net.NetworkTemplate.buildTemplateMobile3gLower; import static android.net.NetworkTemplate.buildTemplateMobile3gLower;
import static android.net.NetworkTemplate.buildTemplateMobile4g; import static android.net.NetworkTemplate.buildTemplateMobile4g;
import static android.net.NetworkTemplate.buildTemplateMobileAll; import static android.net.NetworkTemplate.buildTemplateMobileAll;
import static android.net.NetworkTemplate.buildTemplateWifi; import static android.net.NetworkTemplate.buildTemplateWifiWildcard;
import static android.net.TrafficStats.GB_IN_BYTES; import static android.net.TrafficStats.GB_IN_BYTES;
import static android.net.TrafficStats.MB_IN_BYTES; import static android.net.TrafficStats.MB_IN_BYTES;
import static android.net.TrafficStats.UID_REMOVED; import static android.net.TrafficStats.UID_REMOVED;
import static android.net.TrafficStats.UID_TETHERING; import static android.net.TrafficStats.UID_TETHERING;
import static android.telephony.TelephonyManager.SIM_STATE_READY;
import static android.text.format.DateUtils.FORMAT_ABBREV_MONTH; import static android.text.format.DateUtils.FORMAT_ABBREV_MONTH;
import static android.text.format.DateUtils.FORMAT_SHOW_DATE; import static android.text.format.DateUtils.FORMAT_SHOW_DATE;
import static android.view.ViewGroup.LayoutParams.WRAP_CONTENT; import static android.view.ViewGroup.LayoutParams.WRAP_CONTENT;
@@ -83,6 +84,7 @@ import android.os.ServiceManager;
import android.os.SystemProperties; import android.os.SystemProperties;
import android.os.UserId; import android.os.UserId;
import android.preference.Preference; import android.preference.Preference;
import android.preference.PreferenceActivity;
import android.provider.Settings; import android.provider.Settings;
import android.telephony.TelephonyManager; import android.telephony.TelephonyManager;
import android.text.TextUtils; import android.text.TextUtils;
@@ -127,6 +129,7 @@ import com.android.internal.telephony.Phone;
import com.android.settings.drawable.InsetBoundsDrawable; import com.android.settings.drawable.InsetBoundsDrawable;
import com.android.settings.net.ChartData; import com.android.settings.net.ChartData;
import com.android.settings.net.ChartDataLoader; import com.android.settings.net.ChartDataLoader;
import com.android.settings.net.DataUsageMeteredSettings;
import com.android.settings.net.NetworkPolicyEditor; import com.android.settings.net.NetworkPolicyEditor;
import com.android.settings.net.SummaryForAllUidLoader; import com.android.settings.net.SummaryForAllUidLoader;
import com.android.settings.net.UidDetail; import com.android.settings.net.UidDetail;
@@ -144,8 +147,8 @@ import java.util.Locale;
import libcore.util.Objects; import libcore.util.Objects;
/** /**
* Panel show data usage history across various networks, including options to * Panel showing data usage history across various networks, including options
* inspect based on usage cycle and control through {@link NetworkPolicy}. * to inspect based on usage cycle and control through {@link NetworkPolicy}.
*/ */
public class DataUsageSummary extends Fragment { public class DataUsageSummary extends Fragment {
private static final String TAG = "DataUsage"; private static final String TAG = "DataUsage";
@@ -180,7 +183,7 @@ public class DataUsageSummary extends Fragment {
private INetworkManagementService mNetworkService; private INetworkManagementService mNetworkService;
private INetworkStatsService mStatsService; private INetworkStatsService mStatsService;
private INetworkPolicyManager mPolicyService; private NetworkPolicyManager mPolicyManager;
private ConnectivityManager mConnService; private ConnectivityManager mConnService;
private static final String PREF_FILE = "data_usage"; private static final String PREF_FILE = "data_usage";
@@ -253,19 +256,18 @@ public class DataUsageSummary extends Fragment {
@Override @Override
public void onCreate(Bundle savedInstanceState) { public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState); super.onCreate(savedInstanceState);
final Context context = getActivity();
mNetworkService = INetworkManagementService.Stub.asInterface( mNetworkService = INetworkManagementService.Stub.asInterface(
ServiceManager.getService(Context.NETWORKMANAGEMENT_SERVICE)); ServiceManager.getService(Context.NETWORKMANAGEMENT_SERVICE));
mStatsService = INetworkStatsService.Stub.asInterface( mStatsService = INetworkStatsService.Stub.asInterface(
ServiceManager.getService(Context.NETWORK_STATS_SERVICE)); ServiceManager.getService(Context.NETWORK_STATS_SERVICE));
mPolicyService = INetworkPolicyManager.Stub.asInterface( mPolicyManager = NetworkPolicyManager.from(context);
ServiceManager.getService(Context.NETWORK_POLICY_SERVICE)); mConnService = ConnectivityManager.from(context);
mConnService = (ConnectivityManager) getActivity().getSystemService(
Context.CONNECTIVITY_SERVICE);
mPrefs = getActivity().getSharedPreferences(PREF_FILE, Context.MODE_PRIVATE); mPrefs = getActivity().getSharedPreferences(PREF_FILE, Context.MODE_PRIVATE);
mPolicyEditor = new NetworkPolicyEditor(mPolicyService); mPolicyEditor = new NetworkPolicyEditor(mPolicyManager);
mPolicyEditor.read(); mPolicyEditor.read();
mShowWifi = mPrefs.getBoolean(PREF_SHOW_WIFI, false); mShowWifi = mPrefs.getBoolean(PREF_SHOW_WIFI, false);
@@ -431,19 +433,19 @@ public class DataUsageSummary extends Fragment {
final boolean appDetailMode = isAppDetailMode(); final boolean appDetailMode = isAppDetailMode();
mMenuDataRoaming = menu.findItem(R.id.data_usage_menu_roaming); mMenuDataRoaming = menu.findItem(R.id.data_usage_menu_roaming);
mMenuDataRoaming.setVisible(hasMobileRadio(context) && !appDetailMode); mMenuDataRoaming.setVisible(hasReadyMobileRadio(context) && !appDetailMode);
mMenuDataRoaming.setChecked(getDataRoaming()); mMenuDataRoaming.setChecked(getDataRoaming());
mMenuRestrictBackground = menu.findItem(R.id.data_usage_menu_restrict_background); mMenuRestrictBackground = menu.findItem(R.id.data_usage_menu_restrict_background);
mMenuRestrictBackground.setVisible(hasMobileRadio(context) && !appDetailMode); mMenuRestrictBackground.setVisible(hasReadyMobileRadio(context) && !appDetailMode);
mMenuRestrictBackground.setChecked(getRestrictBackground()); mMenuRestrictBackground.setChecked(mPolicyManager.getRestrictBackground());
final MenuItem split4g = menu.findItem(R.id.data_usage_menu_split_4g); final MenuItem split4g = menu.findItem(R.id.data_usage_menu_split_4g);
split4g.setVisible(hasMobile4gRadio(context) && !appDetailMode); split4g.setVisible(hasMobile4gRadio(context) && !appDetailMode);
split4g.setChecked(isMobilePolicySplit()); split4g.setChecked(isMobilePolicySplit());
final MenuItem showWifi = menu.findItem(R.id.data_usage_menu_show_wifi); final MenuItem showWifi = menu.findItem(R.id.data_usage_menu_show_wifi);
if (hasWifiRadio(context) && hasMobileRadio(context)) { if (hasWifiRadio(context) && hasReadyMobileRadio(context)) {
showWifi.setVisible(!appDetailMode); showWifi.setVisible(!appDetailMode);
showWifi.setChecked(mShowWifi); showWifi.setChecked(mShowWifi);
} else { } else {
@@ -452,13 +454,20 @@ public class DataUsageSummary extends Fragment {
} }
final MenuItem showEthernet = menu.findItem(R.id.data_usage_menu_show_ethernet); final MenuItem showEthernet = menu.findItem(R.id.data_usage_menu_show_ethernet);
if (hasEthernet(context) && hasMobileRadio(context)) { if (hasEthernet(context) && hasReadyMobileRadio(context)) {
showEthernet.setVisible(!appDetailMode); showEthernet.setVisible(!appDetailMode);
showEthernet.setChecked(mShowEthernet); showEthernet.setChecked(mShowEthernet);
} else { } else {
showEthernet.setVisible(false); showEthernet.setVisible(false);
mShowEthernet = true; mShowEthernet = true;
} }
final MenuItem metered = menu.findItem(R.id.data_usage_menu_metered);
if (hasReadyMobileRadio(context) || hasWifiRadio(context)) {
metered.setVisible(!appDetailMode);
} else {
metered.setVisible(false);
}
} }
@Override @Override
@@ -505,6 +514,12 @@ public class DataUsageSummary extends Fragment {
updateTabs(); updateTabs();
return true; return true;
} }
case R.id.data_usage_menu_metered: {
final PreferenceActivity activity = (PreferenceActivity) getActivity();
activity.startPreferencePanel(DataUsageMeteredSettings.class.getCanonicalName(), null,
R.string.data_usage_metered_title, null, this, 0);
return true;
}
} }
return false; return false;
} }
@@ -572,7 +587,7 @@ public class DataUsageSummary extends Fragment {
if (mobileSplit && hasMobile4gRadio(context)) { if (mobileSplit && hasMobile4gRadio(context)) {
mTabHost.addTab(buildTabSpec(TAB_3G, R.string.data_usage_tab_3g)); mTabHost.addTab(buildTabSpec(TAB_3G, R.string.data_usage_tab_3g));
mTabHost.addTab(buildTabSpec(TAB_4G, R.string.data_usage_tab_4g)); mTabHost.addTab(buildTabSpec(TAB_4G, R.string.data_usage_tab_4g));
} else if (hasMobileRadio(context)) { } else if (hasReadyMobileRadio(context)) {
mTabHost.addTab(buildTabSpec(TAB_MOBILE, R.string.data_usage_tab_mobile)); mTabHost.addTab(buildTabSpec(TAB_MOBILE, R.string.data_usage_tab_mobile));
} }
if (mShowWifi && hasWifiRadio(context)) { if (mShowWifi && hasWifiRadio(context)) {
@@ -650,6 +665,9 @@ public class DataUsageSummary extends Fragment {
mDataEnabledView.setVisibility(View.VISIBLE); mDataEnabledView.setVisibility(View.VISIBLE);
// TODO: remove mobile tabs when SIM isn't ready
final TelephonyManager tele = TelephonyManager.from(context);
if (TAB_MOBILE.equals(currentTab)) { if (TAB_MOBILE.equals(currentTab)) {
setPreferenceTitle(mDataEnabledView, R.string.data_usage_enable_mobile); setPreferenceTitle(mDataEnabledView, R.string.data_usage_enable_mobile);
setPreferenceTitle(mDisableAtLimitView, R.string.data_usage_disable_mobile_limit); setPreferenceTitle(mDisableAtLimitView, R.string.data_usage_disable_mobile_limit);
@@ -671,7 +689,7 @@ public class DataUsageSummary extends Fragment {
// wifi doesn't have any controls // wifi doesn't have any controls
mDataEnabledView.setVisibility(View.GONE); mDataEnabledView.setVisibility(View.GONE);
mDisableAtLimitView.setVisibility(View.GONE); mDisableAtLimitView.setVisibility(View.GONE);
mTemplate = buildTemplateWifi(); mTemplate = buildTemplateWifiWildcard();
} else if (TAB_ETHERNET.equals(currentTab)) { } else if (TAB_ETHERNET.equals(currentTab)) {
// ethernet doesn't have any controls // ethernet doesn't have any controls
@@ -755,8 +773,8 @@ public class DataUsageSummary extends Fragment {
updateDetailData(); updateDetailData();
if (UserId.isApp(appId) && !getRestrictBackground() && isBandwidthControlEnabled() if (UserId.isApp(appId) && !mPolicyManager.getRestrictBackground()
&& hasMobileRadio(context)) { && isBandwidthControlEnabled() && hasReadyMobileRadio(context)) {
setPreferenceTitle(mAppRestrictView, R.string.data_usage_app_restrict_background); setPreferenceTitle(mAppRestrictView, R.string.data_usage_app_restrict_background);
setPreferenceSummary(mAppRestrictView, setPreferenceSummary(mAppRestrictView,
getString(R.string.data_usage_app_restrict_background_summary)); getString(R.string.data_usage_app_restrict_background_summary));
@@ -829,48 +847,22 @@ public class DataUsageSummary extends Fragment {
mMenuDataRoaming.setChecked(enabled); mMenuDataRoaming.setChecked(enabled);
} }
private boolean getRestrictBackground() { public void setRestrictBackground(boolean restrictBackground) {
try { mPolicyManager.setRestrictBackground(restrictBackground);
return mPolicyService.getRestrictBackground(); mMenuRestrictBackground.setChecked(restrictBackground);
} catch (RemoteException e) {
Log.w(TAG, "problem talking with policy service: " + e);
return false;
}
}
private void setRestrictBackground(boolean restrictBackground) {
if (LOGD) Log.d(TAG, "setRestrictBackground()");
try {
mPolicyService.setRestrictBackground(restrictBackground);
mMenuRestrictBackground.setChecked(restrictBackground);
} catch (RemoteException e) {
Log.w(TAG, "problem talking with policy service: " + e);
}
} }
private boolean getAppRestrictBackground() { private boolean getAppRestrictBackground() {
final int appId = mCurrentApp.appId; final int appId = mCurrentApp.appId;
final int uidPolicy; final int uidPolicy = mPolicyManager.getAppPolicy(appId);
try {
uidPolicy = mPolicyService.getAppPolicy(appId);
} catch (RemoteException e) {
// since we can't do much without policy, we bail hard.
throw new RuntimeException("problem reading network policy", e);
}
return (uidPolicy & POLICY_REJECT_METERED_BACKGROUND) != 0; return (uidPolicy & POLICY_REJECT_METERED_BACKGROUND) != 0;
} }
private void setAppRestrictBackground(boolean restrictBackground) { private void setAppRestrictBackground(boolean restrictBackground) {
if (LOGD) Log.d(TAG, "setAppRestrictBackground()"); if (LOGD) Log.d(TAG, "setAppRestrictBackground()");
final int appId = mCurrentApp.appId; final int appId = mCurrentApp.appId;
try { mPolicyManager.setAppPolicy(appId,
mPolicyService.setAppPolicy(appId, restrictBackground ? POLICY_REJECT_METERED_BACKGROUND : POLICY_NONE);
restrictBackground ? POLICY_REJECT_METERED_BACKGROUND : POLICY_NONE);
} catch (RemoteException e) {
throw new RuntimeException("unable to save policy", e);
}
mAppRestrict.setChecked(restrictBackground); mAppRestrict.setChecked(restrictBackground);
} }
@@ -1201,23 +1193,25 @@ public class DataUsageSummary extends Fragment {
private boolean isMobilePolicySplit() { private boolean isMobilePolicySplit() {
final Context context = getActivity(); final Context context = getActivity();
if (hasMobileRadio(context)) { if (hasReadyMobileRadio(context)) {
final String subscriberId = getActiveSubscriberId(context); final TelephonyManager tele = TelephonyManager.from(context);
return mPolicyEditor.isMobilePolicySplit(subscriberId); return mPolicyEditor.isMobilePolicySplit(getActiveSubscriberId(context));
} else { } else {
return false; return false;
} }
} }
private void setMobilePolicySplit(boolean split) { private void setMobilePolicySplit(boolean split) {
final String subscriberId = getActiveSubscriberId(getActivity()); final Context context = getActivity();
mPolicyEditor.setMobilePolicySplit(subscriberId, split); if (hasReadyMobileRadio(context)) {
final TelephonyManager tele = TelephonyManager.from(context);
mPolicyEditor.setMobilePolicySplit(getActiveSubscriberId(context), split);
}
} }
private static String getActiveSubscriberId(Context context) { private static String getActiveSubscriberId(Context context) {
final TelephonyManager telephony = (TelephonyManager) context.getSystemService( final TelephonyManager tele = TelephonyManager.from(context);
Context.TELEPHONY_SERVICE); final String actualSubscriberId = tele.getSubscriberId();
final String actualSubscriberId = telephony.getSubscriberId();
return SystemProperties.get(TEST_SUBSCRIBER_PROP, actualSubscriberId); return SystemProperties.get(TEST_SUBSCRIBER_PROP, actualSubscriberId);
} }
@@ -2048,22 +2042,24 @@ public class DataUsageSummary extends Fragment {
} }
/** /**
* Test if device has a mobile data radio. * Test if device has a mobile data radio with SIM in ready state.
*/ */
private static boolean hasMobileRadio(Context context) { public static boolean hasReadyMobileRadio(Context context) {
if (TEST_RADIOS) { if (TEST_RADIOS) {
return SystemProperties.get(TEST_RADIOS_PROP).contains("mobile"); return SystemProperties.get(TEST_RADIOS_PROP).contains("mobile");
} }
final ConnectivityManager conn = (ConnectivityManager) context.getSystemService( final ConnectivityManager conn = ConnectivityManager.from(context);
Context.CONNECTIVITY_SERVICE); final TelephonyManager tele = TelephonyManager.from(context);
return conn.isNetworkSupported(TYPE_MOBILE);
// require both supported network and ready SIM
return conn.isNetworkSupported(TYPE_MOBILE) && tele.getSimState() == SIM_STATE_READY;
} }
/** /**
* Test if device has a mobile 4G data radio. * Test if device has a mobile 4G data radio.
*/ */
private static boolean hasMobile4gRadio(Context context) { public static boolean hasMobile4gRadio(Context context) {
if (!NetworkPolicyEditor.ENABLE_SPLIT_POLICIES) { if (!NetworkPolicyEditor.ENABLE_SPLIT_POLICIES) {
return false; return false;
} }
@@ -2071,39 +2067,35 @@ public class DataUsageSummary extends Fragment {
return SystemProperties.get(TEST_RADIOS_PROP).contains("4g"); return SystemProperties.get(TEST_RADIOS_PROP).contains("4g");
} }
final ConnectivityManager conn = (ConnectivityManager) context.getSystemService( final ConnectivityManager conn = ConnectivityManager.from(context);
Context.CONNECTIVITY_SERVICE); final TelephonyManager tele = TelephonyManager.from(context);
final TelephonyManager telephony = (TelephonyManager) context.getSystemService(
Context.TELEPHONY_SERVICE);
final boolean hasWimax = conn.isNetworkSupported(TYPE_WIMAX); final boolean hasWimax = conn.isNetworkSupported(TYPE_WIMAX);
final boolean hasLte = telephony.getLteOnCdmaMode() == Phone.LTE_ON_CDMA_TRUE; final boolean hasLte = tele.getLteOnCdmaMode() == Phone.LTE_ON_CDMA_TRUE;
return hasWimax || hasLte; return hasWimax || hasLte;
} }
/** /**
* Test if device has a Wi-Fi data radio. * Test if device has a Wi-Fi data radio.
*/ */
private static boolean hasWifiRadio(Context context) { public static boolean hasWifiRadio(Context context) {
if (TEST_RADIOS) { if (TEST_RADIOS) {
return SystemProperties.get(TEST_RADIOS_PROP).contains("wifi"); return SystemProperties.get(TEST_RADIOS_PROP).contains("wifi");
} }
final ConnectivityManager conn = (ConnectivityManager) context.getSystemService( final ConnectivityManager conn = ConnectivityManager.from(context);
Context.CONNECTIVITY_SERVICE);
return conn.isNetworkSupported(TYPE_WIFI); return conn.isNetworkSupported(TYPE_WIFI);
} }
/** /**
* Test if device has an ethernet network connection. * Test if device has an ethernet network connection.
*/ */
private static boolean hasEthernet(Context context) { public static boolean hasEthernet(Context context) {
if (TEST_RADIOS) { if (TEST_RADIOS) {
return SystemProperties.get(TEST_RADIOS_PROP).contains("ethernet"); return SystemProperties.get(TEST_RADIOS_PROP).contains("ethernet");
} }
final ConnectivityManager conn = (ConnectivityManager) context.getSystemService( final ConnectivityManager conn = ConnectivityManager.from(context);
Context.CONNECTIVITY_SERVICE);
return conn.isNetworkSupported(TYPE_ETHERNET); return conn.isNetworkSupported(TYPE_ETHERNET);
} }
@@ -2138,6 +2130,7 @@ public class DataUsageSummary extends Fragment {
* Build string describing currently limited networks, which defines when * Build string describing currently limited networks, which defines when
* background data is restricted. * background data is restricted.
*/ */
@Deprecated
private CharSequence buildLimitedNetworksString() { private CharSequence buildLimitedNetworksString() {
final List<CharSequence> limited = buildLimitedNetworksList(); final List<CharSequence> limited = buildLimitedNetworksList();
@@ -2153,22 +2146,28 @@ public class DataUsageSummary extends Fragment {
* Build list of currently limited networks, which defines when background * Build list of currently limited networks, which defines when background
* data is restricted. * data is restricted.
*/ */
@Deprecated
private List<CharSequence> buildLimitedNetworksList() { private List<CharSequence> buildLimitedNetworksList() {
final Context context = getActivity(); final Context context = getActivity();
final String subscriberId = getActiveSubscriberId(context);
// build combined list of all limited networks // build combined list of all limited networks
final ArrayList<CharSequence> limited = Lists.newArrayList(); final ArrayList<CharSequence> limited = Lists.newArrayList();
if (mPolicyEditor.hasLimitedPolicy(buildTemplateMobileAll(subscriberId))) {
limited.add(getText(R.string.data_usage_list_mobile)); final TelephonyManager tele = TelephonyManager.from(context);
if (tele.getSimState() == SIM_STATE_READY) {
final String subscriberId = getActiveSubscriberId(context);
if (mPolicyEditor.hasLimitedPolicy(buildTemplateMobileAll(subscriberId))) {
limited.add(getText(R.string.data_usage_list_mobile));
}
if (mPolicyEditor.hasLimitedPolicy(buildTemplateMobile3gLower(subscriberId))) {
limited.add(getText(R.string.data_usage_tab_3g));
}
if (mPolicyEditor.hasLimitedPolicy(buildTemplateMobile4g(subscriberId))) {
limited.add(getText(R.string.data_usage_tab_4g));
}
} }
if (mPolicyEditor.hasLimitedPolicy(buildTemplateMobile3gLower(subscriberId))) {
limited.add(getText(R.string.data_usage_tab_3g)); if (mPolicyEditor.hasLimitedPolicy(buildTemplateWifiWildcard())) {
}
if (mPolicyEditor.hasLimitedPolicy(buildTemplateMobile4g(subscriberId))) {
limited.add(getText(R.string.data_usage_tab_4g));
}
if (mPolicyEditor.hasLimitedPolicy(buildTemplateWifi())) {
limited.add(getText(R.string.data_usage_tab_wifi)); limited.add(getText(R.string.data_usage_tab_wifi));
} }
if (mPolicyEditor.hasLimitedPolicy(buildTemplateEthernet())) { if (mPolicyEditor.hasLimitedPolicy(buildTemplateEthernet())) {

View File

@@ -0,0 +1,133 @@
/*
* Copyright (C) 2012 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.settings.net;
import static com.android.settings.DataUsageSummary.hasReadyMobileRadio;
import static com.android.settings.DataUsageSummary.hasWifiRadio;
import android.content.Context;
import android.net.NetworkPolicy;
import android.net.NetworkPolicyManager;
import android.net.NetworkTemplate;
import android.net.wifi.WifiConfiguration;
import android.net.wifi.WifiManager;
import android.os.Bundle;
import android.preference.CheckBoxPreference;
import android.preference.Preference;
import android.preference.PreferenceCategory;
import android.telephony.TelephonyManager;
import com.android.settings.R;
import com.android.settings.SettingsPreferenceFragment;
/**
* Panel to configure {@link NetworkPolicy#metered} for networks.
*/
public class DataUsageMeteredSettings extends SettingsPreferenceFragment {
private NetworkPolicyManager mPolicyManager;
private WifiManager mWifiManager;
private NetworkPolicyEditor mPolicyEditor;
private PreferenceCategory mMobileCategory;
private PreferenceCategory mWifiCategory;
@Override
public void onCreate(Bundle icicle) {
super.onCreate(icicle);
final Context context = getActivity();
mPolicyManager = NetworkPolicyManager.from(context);
mWifiManager = (WifiManager) context.getSystemService(Context.WIFI_SERVICE);
mPolicyEditor = new NetworkPolicyEditor(mPolicyManager);
mPolicyEditor.read();
addPreferencesFromResource(R.xml.data_usage_metered_prefs);
mMobileCategory = (PreferenceCategory) findPreference("mobile");
mWifiCategory = (PreferenceCategory) findPreference("wifi");
updateNetworks(context);
}
private void updateNetworks(Context context) {
if (hasReadyMobileRadio(context)) {
mMobileCategory.removeAll();
mMobileCategory.addPreference(buildMobilePref(context));
} else {
getPreferenceScreen().removePreference(mMobileCategory);
}
if (hasWifiRadio(context)) {
mWifiCategory.removeAll();
for (WifiConfiguration config : mWifiManager.getConfiguredNetworks()) {
if (config.SSID != null) {
mWifiCategory.addPreference(buildWifiPref(context, config));
}
}
} else {
getPreferenceScreen().removePreference(mWifiCategory);
}
}
private Preference buildMobilePref(Context context) {
final TelephonyManager tele = TelephonyManager.from(context);
final NetworkTemplate template = NetworkTemplate.buildTemplateMobileAll(
tele.getSubscriberId());
final MeteredPreference pref = new MeteredPreference(context, template);
pref.setTitle(tele.getNetworkOperatorName());
return pref;
}
private Preference buildWifiPref(Context context, WifiConfiguration config) {
final String networkId = removeDoubleQuotes(config.SSID);
final NetworkTemplate template = NetworkTemplate.buildTemplateWifi(networkId);
final MeteredPreference pref = new MeteredPreference(context, template);
pref.setTitle(networkId);
return pref;
}
private class MeteredPreference extends CheckBoxPreference {
private final NetworkTemplate mTemplate;
public MeteredPreference(Context context, NetworkTemplate template) {
super(context);
mTemplate = template;
setPersistent(false);
setChecked(mPolicyEditor.getPolicyMetered(mTemplate));
}
@Override
protected void notifyChanged() {
super.notifyChanged();
mPolicyEditor.setPolicyMetered(mTemplate, isChecked());
}
}
private static String removeDoubleQuotes(String string) {
final int length = string.length();
if ((length > 1) && (string.charAt(0) == '"') && (string.charAt(length - 1) == '"')) {
return string.substring(1, length - 1);
}
return string;
}
}

View File

@@ -16,6 +16,7 @@
package com.android.settings.net; package com.android.settings.net;
import static android.net.NetworkPolicy.CYCLE_NONE;
import static android.net.NetworkPolicy.LIMIT_DISABLED; import static android.net.NetworkPolicy.LIMIT_DISABLED;
import static android.net.NetworkPolicy.SNOOZE_NEVER; import static android.net.NetworkPolicy.SNOOZE_NEVER;
import static android.net.NetworkPolicy.WARNING_DISABLED; import static android.net.NetworkPolicy.WARNING_DISABLED;
@@ -27,11 +28,10 @@ import static android.net.NetworkTemplate.buildTemplateMobile4g;
import static android.net.NetworkTemplate.buildTemplateMobileAll; import static android.net.NetworkTemplate.buildTemplateMobileAll;
import static com.android.internal.util.Preconditions.checkNotNull; import static com.android.internal.util.Preconditions.checkNotNull;
import android.net.INetworkPolicyManager;
import android.net.NetworkPolicy; import android.net.NetworkPolicy;
import android.net.NetworkPolicyManager;
import android.net.NetworkTemplate; import android.net.NetworkTemplate;
import android.os.AsyncTask; import android.os.AsyncTask;
import android.os.RemoteException;
import android.text.format.Time; import android.text.format.Time;
import com.android.internal.util.Objects; import com.android.internal.util.Objects;
@@ -43,27 +43,23 @@ import java.util.HashSet;
/** /**
* Utility class to modify list of {@link NetworkPolicy}. Specifically knows * Utility class to modify list of {@link NetworkPolicy}. Specifically knows
* about which policies can coexist. Not thread safe. * about which policies can coexist. This editor offers thread safety when
* talking with {@link NetworkPolicyManager}.
*/ */
public class NetworkPolicyEditor { public class NetworkPolicyEditor {
// TODO: be more robust when missing policies from service // TODO: be more robust when missing policies from service
public static final boolean ENABLE_SPLIT_POLICIES = false; public static final boolean ENABLE_SPLIT_POLICIES = false;
private INetworkPolicyManager mPolicyService; private NetworkPolicyManager mPolicyManager;
private ArrayList<NetworkPolicy> mPolicies = Lists.newArrayList(); private ArrayList<NetworkPolicy> mPolicies = Lists.newArrayList();
public NetworkPolicyEditor(INetworkPolicyManager policyService) { public NetworkPolicyEditor(NetworkPolicyManager policyManager) {
mPolicyService = checkNotNull(policyService); mPolicyManager = checkNotNull(policyManager);
} }
public void read() { public void read() {
final NetworkPolicy[] policies; final NetworkPolicy[] policies = mPolicyManager.getNetworkPolicies();
try {
policies = mPolicyService.getNetworkPolicies();
} catch (RemoteException e) {
throw new RuntimeException("problem reading policies", e);
}
boolean modified = false; boolean modified = false;
mPolicies.clear(); mPolicies.clear();
@@ -78,12 +74,6 @@ public class NetworkPolicyEditor {
modified = true; modified = true;
} }
// drop any WIFI policies that were defined
if (policy.template.getMatchRule() == MATCH_WIFI) {
modified = true;
continue;
}
mPolicies.add(policy); mPolicies.add(policy);
} }
@@ -109,11 +99,7 @@ public class NetworkPolicyEditor {
} }
public void write(NetworkPolicy[] policies) { public void write(NetworkPolicy[] policies) {
try { mPolicyManager.setNetworkPolicies(policies);
mPolicyService.setNetworkPolicies(policies);
} catch (RemoteException e) {
throw new RuntimeException("problem writing policies", e);
}
} }
public boolean hasLimitedPolicy(NetworkTemplate template) { public boolean hasLimitedPolicy(NetworkTemplate template) {
@@ -142,13 +128,24 @@ public class NetworkPolicyEditor {
@Deprecated @Deprecated
private static NetworkPolicy buildDefaultPolicy(NetworkTemplate template) { private static NetworkPolicy buildDefaultPolicy(NetworkTemplate template) {
// TODO: move this into framework to share with NetworkPolicyManagerService // TODO: move this into framework to share with NetworkPolicyManagerService
final Time time = new Time(); final int cycleDay;
time.setToNow(); final String cycleTimezone;
final int cycleDay = time.monthDay; final boolean metered;
final String cycleTimezone = time.timezone;
if (template.getMatchRule() == MATCH_WIFI) {
cycleDay = CYCLE_NONE;
cycleTimezone = Time.TIMEZONE_UTC;
metered = false;
} else {
final Time time = new Time();
time.setToNow();
cycleDay = time.monthDay;
cycleTimezone = time.timezone;
metered = true;
}
return new NetworkPolicy(template, cycleDay, cycleTimezone, WARNING_DISABLED, return new NetworkPolicy(template, cycleDay, cycleTimezone, WARNING_DISABLED,
LIMIT_DISABLED, SNOOZE_NEVER, SNOOZE_NEVER, true, false); LIMIT_DISABLED, SNOOZE_NEVER, SNOOZE_NEVER, metered, true);
} }
public int getPolicyCycleDay(NetworkTemplate template) { public int getPolicyCycleDay(NetworkTemplate template) {
@@ -188,6 +185,52 @@ public class NetworkPolicyEditor {
writeAsync(); writeAsync();
} }
public boolean getPolicyMetered(NetworkTemplate template) {
final NetworkPolicy policy = getPolicy(template);
if (policy != null) {
return policy.metered;
} else {
return false;
}
}
public void setPolicyMetered(NetworkTemplate template, boolean metered) {
boolean modified = false;
NetworkPolicy policy = getPolicy(template);
if (metered) {
if (policy == null) {
policy = buildDefaultPolicy(template);
policy.metered = true;
policy.inferred = false;
mPolicies.add(policy);
modified = true;
} else if (!policy.metered) {
policy.metered = true;
policy.inferred = false;
modified = true;
}
} else {
if (policy == null) {
// ignore when policy doesn't exist
} else if (policy.template.getMatchRule() == MATCH_WIFI
&& policy.warningBytes == WARNING_DISABLED
&& policy.limitBytes == LIMIT_DISABLED) {
// when WIFI goes unmetered, and no other warning/limit for
// policy, clean it up.
mPolicies.remove(policy);
modified = true;
} else if (policy.metered) {
policy.metered = false;
policy.inferred = false;
modified = true;
}
}
if (modified) writeAsync();
}
/** /**
* Remove any split {@link NetworkPolicy}. * Remove any split {@link NetworkPolicy}.
*/ */