前言
android 源码中一般都自带DHCP上网的,静态IP上网是没有的。这就需要我们自己添加了,
因为之前搞过6.0的静态IP功能,同样是 MTK 平台的,差异还是有点大的,
对比分析修改完成了需求,特此分享一下,避免更多的人踩坑。
如果这篇文章帮到你,欢迎点赞和转发,请注明原文地址
Android8.1
先上效果图,毕竟没图你说个锤子
动图
如图所示在 settings 中增加以太网的配置项
1、vendor\mediatek\proprietary\packages\apps\MtkSettings\res\xml\network_and_internet.xml
<com.android.settingslib.RestrictedPreference android:key="ethernet_settings" android:title="@string/ethernet_settings_title" android:summary="@string/summary_placeholder" android:icon="@drawable/ic_ethernet_cell" android:fragment="com.android.settings.ethernet.EthernetSettings" android:order="-17"/>
在 mobile_network_settings 和 tether_settings 之间增加如上代码
对应的 icon 资源文件是我从 SystemUI 中拷贝过来的,稍微调整了下大小,也贴给你们吧
2、
vendor\mediatek\proprietary\packages\apps\MtkSettings\res\drawable\ic_ethernet_cell.xml
<vector xmlns:android="http://schemas.android.com/apk/res/android" android:autoMirrored="true" android:width="24dp" android:height="24dp" android:viewportWidth="48" android:viewportHeight="48" android:tint="?android:attr/colorControlNormal"> <path android:fillColor="#fff" android:pathData="M15.54 13.52l-3.08-2.55L1.64 24l10.82 13.04 3.08-2.55L6.84 24l8.7-10.48zM14 26h4v-4h-4v4zm20-4h-4v4h4v-4zm-12 4h4v-4h-4v4zm13.54-15.04l-3.08 2.55L41.16 24l-8.7 10.48 3.08 2.55L46.36 24 35.54 10.96z"/> </vector>
vendor\mediatek\proprietary\packages\apps\MtkSettings\res\values\strings.xml
<string name="ethernet_ip_settings_invalid_ip">"Please fill in the correct format."</string> <string name="eth_ip_settings_please_complete_settings">"Network information is not complete, please fill in the complete"</string> <string name="save_satic_ethernet">"Save"</string> <string name="enthernet_static">"Use static settings"</string> <string name="enthernet_ip_address">"IP address"</string> <string name="enthernet_gateway">"gateway"</string> <string name="enthernet_netmask">"Subnet mask"</string> <string name="enthernet_dns1">"domain1"</string> <string name="enthernet_dns2">"domain2"</string> <string name="ethernet_quick_toggle_title">"Ethernet"</string> <string name="open_ethernet">"Open Ethernet"</string> <string name="ethernet_static_ip_settings_title">"Setting Ethernet"</string> <string name="ethernet_settings">"Ethernet"</string> <string name="ethernet_settings_title">"Ethernet"</string>
vendor\mediatek\proprietary\packages\apps\MtkSettings\res\values-zh-rCN\strings.xml
<string name="ethernet_ip_settings_invalid_ip">"请填写正确的格式"</string> <string name="save_satic_ethernet">"保存"</string> <string name="eth_ip_settings_please_complete_settings">"网络信息不完整,请填写完整"</string> <string name="enthernet_static">"使用静态设置"</string> <string name="enthernet_ip_address">"IP地址"</string> <string name="enthernet_gateway">"网关"</string> <string name="enthernet_netmask">"子网掩码"</string> <string name="enthernet_dns1">"域名1"</string> <string name="enthernet_dns2">"域名2"</string> <string name="ethernet_quick_toggle_title">"以太网"</string> <string name="open_ethernet">"打开以太网"</string> <string name="ethernet_static_ip_settings_title">"配置以太网"</string> <string name="ethernet_settings">"以太网"</string> <string name="ethernet_settings_title">"以太网"</string>
3、增加对应设置的两个布局xml
vendor\mediatek\proprietary\packages\apps\MtkSettings\res\xml\ethernet_settings.xml
<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android" android:title="@string/ethernet_settings"> <SwitchPreference android:key="ethernet" android:title="@string/ethernet_quick_toggle_title" android:summary="@string/open_ethernet"/> <PreferenceScreen android:dependency="ethernet" android:fragment="com.android.settings.ethernet.EthernetStaticIP" android:key="ethernet_static_ip" android:title="@string/ethernet_static_ip_settings_title" /> </PreferenceScreen>
vendor\mediatek\proprietary\packages\apps\MtkSettings\res\xml\ethernet_static_ip.xml
<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android" android:title="@string/ethernet_static_ip_settings_title"> <SwitchPreference android:key="use_static_ip" android:title="@string/enthernet_static" android:persistent="false"/> <EditTextPreference android:dependency="use_static_ip" android:key="ip_address" android:title="@string/enthernet_ip_address" android:persistent="false" android:singleLine="true"/> <EditTextPreference android:dependency="use_static_ip" android:key="gateway" android:title="@string/enthernet_gateway" android:persistent="false" android:singleLine="true"/> <EditTextPreference android:dependency="use_static_ip" android:key="netmask" android:title="@string/enthernet_netmask" android:persistent="false" android:singleLine="true" /> <EditTextPreference android:dependency="use_static_ip" android:key="dns1" android:title="@string/enthernet_dns1" android:persistent="false" android:singleLine="true"/> <EditTextPreference android:dependency="use_static_ip" android:key="dns2" android:title="@string/enthernet_dns2" android:persistent="false" android:singleLine="true"/> </PreferenceScreen>
4、增加对应两个布局的 java 控制类
vendor\mediatek\proprietary\packages\apps\MtkSettings\src\com\android\settings\ethernet\EthernetSettings.java
package com.android.settings.ethernet; import android.app.Activity; import android.content.ComponentName; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; import android.content.BroadcastReceiver; import android.content.pm.PackageManager; import android.content.pm.ResolveInfo; import android.os.Build; import android.os.Bundle; import android.os.SystemClock; import android.os.SystemProperties; import android.support.v7.preference.Preference; import android.support.v7.preference.PreferenceGroup; import android.support.v7.preference.PreferenceScreen; import android.support.v7.preference.CheckBoxPreference; import android.support.v14.preference.SwitchPreference; import android.provider.Settings; import android.provider.Settings.System; import android.provider.Settings.Secure; import android.util.Log; import com.android.internal.logging.nano.MetricsProto.MetricsEvent; import android.widget.Toast; import java.io.BufferedReader; import java.io.FileReader; import java.io.File; import java.io.IOException; import java.util.List; import java.util.regex.Matcher; import java.util.regex.Pattern; import java.net.InetAddress; import android.net.EthernetManager; import android.net.StaticIpConfiguration; import android.net.LinkAddress; import android.net.IpConfiguration; import com.android.settings.SettingsPreferenceFragment; import com.android.settings.R; public class EthernetSettings extends SettingsPreferenceFragment implements Preference.OnPreferenceChangeListener{ private static final String TAG = "EthernetSettings"; private static final String USE_ETHERNET_SETTINGS = "ethernet"; public static final String IS_ETHERNET_OPEN = Settings.IS_ETHERNET_OPEN; private SwitchPreference mUseEthernet; private IntentFilter mIntentFilter; private boolean isEthernetEnabled() { return Settings.System.getInt(getActivity().getContentResolver(), IS_ETHERNET_OPEN,0) == 1 ? true : false; } @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); addPreferencesFromResource(R.xml.ethernet_settings); mUseEthernet = (SwitchPreference) findPreference(USE_ETHERNET_SETTINGS); mUseEthernet.setOnPreferenceChangeListener(this); if(isEthernetEnabled()) { mUseEthernet.setChecked(true); } else { mUseEthernet.setChecked(false); } File f = new File("sys/class/net/eth0/address"); if (f.exists()) { mUseEthernet.setEnabled(true); } else { mUseEthernet.setEnabled(false); } } @Override public void onResume() { super.onResume(); } @Override public int getMetricsCategory(){return MetricsEvent.ETHERNET;} @Override public void onPause() { super.onPause(); } @Override public boolean onPreferenceChange(Preference preference, Object value) { boolean result = true; final String key = preference.getKey(); if (USE_ETHERNET_SETTINGS.equals(key)) { Settings.System.putInt(getActivity().getContentResolver(), IS_ETHERNET_OPEN, ((Boolean) value) ? 1 : 0); } return result; } }
vendor\mediatek\proprietary\packages\apps\MtkSettings\src\com\android\settings\ethernet\EthernetStaticIP.java
package com.android.settings.ethernet; import android.app.Dialog; import android.content.DialogInterface; import android.content.Intent; import android.content.ContentResolver; import android.os.Bundle; import android.support.v7.preference.Preference; import android.preference.PreferenceActivity; import android.support.v7.preference.PreferenceScreen; import android.support.v7.preference.CheckBoxPreference; import android.support.v7.preference.EditTextPreference; import android.support.v14.preference.SwitchPreference; import android.provider.Settings; import android.provider.Settings.Secure; import android.util.Log; import android.view.ContextMenu; import android.view.Menu; import android.view.MenuInflater; import android.view.MenuItem; import android.view.View; import android.view.ContextMenu.ContextMenuInfo; import android.widget.AdapterView; import android.widget.Toast; import android.widget.AdapterView.AdapterContextMenuInfo; import android.text.TextUtils; import java.util.Set; import java.util.WeakHashMap; import java.util.Formatter; import java.net.InetAddress; import android.net.EthernetManager; import android.net.StaticIpConfiguration; import android.net.LinkAddress; import android.net.IpConfiguration; import android.net.IpConfiguration.IpAssignment; import android.net.IpConfiguration.ProxySettings; import android.net.NetworkInfo.DetailedState; import android.content.BroadcastReceiver; import android.content.IntentFilter; import android.content.Context; import android.net.NetworkInfo; import android.view.KeyEvent; import android.view.Menu; import android.view.MenuItem; import android.app.AlertDialog; import com.android.settings.SettingsPreferenceFragment; import com.android.settings.R; import com.android.internal.logging.nano.MetricsProto.MetricsEvent; public class EthernetStaticIP extends SettingsPreferenceFragment implements Preference.OnPreferenceChangeListener { private static final String TAG = "EthernetStaticIP"; public static final boolean DEBUG = false; private static void LOG(String msg) { if ( DEBUG ) { Log.d(TAG, msg); } } /*-------------------------------------------------------*/ private static final String KEY_USE_STATIC_IP = "use_static_ip"; private static final String KEY_IP_ADDRESS = "ip_address"; private static final String KEY_GATEWAY = "gateway"; private static final String KEY_NETMASK = "netmask"; private static final String KEY_DNS1 = "dns1"; private static final String KEY_DNS2 = "dns2"; public static final String ETHERNET_USE_STATIC_IP = Settings.IS_ETHERNET_STATUC_OPEN; private static final int MENU_ITEM_SAVE = Menu.FIRST; private static final int MENU_ITEM_CANCEL = Menu.FIRST + 1; private String[] mSettingNames = { Settings.ETHERNET_STATIC_IP, Settings.ETHERNET_STATIC_GATEWAY, Settings.ETHERNET_STATIC_NETMASK, Settings.ETHERNET_STATIC_DNS1, Settings.ETHERNET_STATIC_DNS2 }; private String[] mPreferenceKeys = { KEY_IP_ADDRESS, KEY_GATEWAY, KEY_NETMASK, KEY_DNS1, KEY_DNS2, }; /*-------------------------------------------------------*/ private SwitchPreference mUseStaticIpSwitch; private StaticIpConfiguration mStaticIpConfiguration; private IpConfiguration mIpConfiguration; private EthernetManager mEthernetManager; private boolean isOnPause = false; private boolean chageState = false; public EthernetStaticIP() { } @Override public void onActivityCreated(Bundle savedInstanceState){ super.onActivityCreated(savedInstanceState); addPreferencesFromResource(R.xml.ethernet_static_ip); mUseStaticIpSwitch = (SwitchPreference)findPreference(KEY_USE_STATIC_IP); mUseStaticIpSwitch.setOnPreferenceChangeListener(this); for ( int i = 0; i < mPreferenceKeys.length; i++ ) { Preference preference = findPreference(mPreferenceKeys[i] ); preference.setOnPreferenceChangeListener(this); } setHasOptionsMenu(true); } @Override public void onResume() { super.onResume(); if(!isOnPause) { updateIpSettingsInfo(); } isOnPause = false; } @Override public int getMetricsCategory(){return MetricsEvent.ETHERNET_STATIC;} @Override public void onPause() { isOnPause = true; super.onPause(); } @Override public void onDestroy() { super.onDestroy(); } @Override public void onSaveInstanceState(Bundle outState) { super.onSaveInstanceState(outState); } @Override public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) { menu.add(Menu.NONE, MENU_ITEM_SAVE, 0, R.string.save_satic_ethernet) .setEnabled(true) .setShowAsAction(MenuItem.SHOW_AS_ACTION_ALWAYS); super.onCreateOptionsMenu(menu, inflater); } @Override public boolean onOptionsItemSelected(MenuItem item) { switch (item.getItemId()) { case MENU_ITEM_SAVE: saveIpSettingsInfo(); if(isIpDataInUiComplete()) finish(); return true; case MENU_ITEM_CANCEL: finish(); return true; } return super.onOptionsItemSelected(item); } private void updateIpSettingsInfo() { LOG("Static IP status updateIpSettingsInfo"); ContentResolver contentResolver = getContentResolver(); mUseStaticIpSwitch.setChecked(Settings.System.getInt(contentResolver, ETHERNET_USE_STATIC_IP, 0) != 0); for (int i = 0; i < mSettingNames.length; i++) { EditTextPreference preference = (EditTextPreference) findPreference(mPreferenceKeys[i]); String settingValue = Settings.System.getString(contentResolver, mSettingNames[i]); preference.setText(settingValue); preference.setSummary(settingValue); } } private void saveIpSettingsInfo() { ContentResolver contentResolver = getContentResolver(); /* if(!chageState) return; */ if(!isIpDataInUiComplete()) { Toast.makeText(getActivity(), R.string.eth_ip_settings_please_complete_settings, Toast.LENGTH_LONG).show(); return; } mIpConfiguration = new IpConfiguration(); mStaticIpConfiguration = new StaticIpConfiguration(); for (int i = 0; i < mSettingNames.length; i++) { EditTextPreference preference = (EditTextPreference) findPreference(mPreferenceKeys[i]); String text = preference.getText(); try { switch (mPreferenceKeys[i]) { case KEY_IP_ADDRESS: mStaticIpConfiguration.ipAddress = new LinkAddress(InetAddress.getByName(text), 24); break; case KEY_GATEWAY: mStaticIpConfiguration.gateway = InetAddress.getByName(text); break; case KEY_NETMASK: mStaticIpConfiguration.domains = text; break; case KEY_DNS1: mStaticIpConfiguration.dnsServers.add(InetAddress.getByName(text)); break; case KEY_DNS2: mStaticIpConfiguration.dnsServers.add(InetAddress.getByName(text)); break; } } catch (Exception e) { e.printStackTrace(); } if ( null == text || TextUtils.isEmpty(text) ) { Settings.System.putString(contentResolver, mSettingNames[i], null); } else { Settings.System.putString(contentResolver, mSettingNames[i], text); } } mIpConfiguration.ipAssignment = IpAssignment.STATIC; mIpConfiguration.proxySettings = ProxySettings.STATIC; mIpConfiguration.staticIpConfiguration = mStaticIpConfiguration; mEthernetManager = (EthernetManager) getSystemService(Context.ETHERNET_SERVICE); if (mUseStaticIpSwitch.isChecked()) mEthernetManager.setConfiguration(mIpConfiguration); Settings.System.putInt(contentResolver,ETHERNET_USE_STATIC_IP, mUseStaticIpSwitch.isChecked() ? 1 : 0); // disable ethernet boolean enable = Secure.getInt(getContentResolver(), "isEnthernetOn", 1) == 1; LOG("notify Secure.ETHERNET_ON changed. enable = " + enable); if(enable) { LOG("first disable"); Secure.putInt(getContentResolver(), "isEnthernetOn", 0); try { Thread.sleep(500); } catch (InterruptedException e) {} LOG("second enable"); Secure.putInt(getContentResolver(), "isEnthernetOn", 1); } } @Override public boolean onPreferenceTreeClick(Preference preference) { boolean result = true; LOG("onPreferenceTreeClick() chageState = " + chageState); chageState = true; return result; } @Override public boolean onPreferenceChange(Preference preference, Object newValue) { boolean result = true; String key = preference.getKey(); LOG("onPreferenceChange() : key = " + key); if ( null == key ) { return true; }else if (key.equals(KEY_USE_STATIC_IP)) { }else if ( key.equals(KEY_IP_ADDRESS) || key.equals(KEY_GATEWAY) || key.equals(KEY_NETMASK) || key.equals(KEY_DNS1) || key.equals(KEY_DNS2) ) { String value = (String) newValue; LOG("onPreferenceChange() : value = " + value); if ( TextUtils.isEmpty(value) ) { ( (EditTextPreference)preference).setText(value); preference.setSummary(value); result = true; } else if ( !isValidIpAddress(value) ) { LOG("onPreferenceChange() : IP address user inputed is INVALID." ); Toast.makeText(getActivity(), R.string.ethernet_ip_settings_invalid_ip, Toast.LENGTH_LONG).show(); return false; } else { ( (EditTextPreference)preference).setText(value); preference.setSummary(value); result = true; } } return result; } private boolean isValidIpAddress(String value) { int start = 0; int end = value.indexOf('.'); int numBlocks = 0; while (start < value.length()) { if ( -1 == end ) { end = value.length(); } try { int block = Integer.parseInt(value.substring(start, end)); if ((block > 255) || (block < 0)) { Log.w(TAG, "isValidIpAddress() : invalid 'block', block = " + block); return false; } } catch (NumberFormatException e) { Log.w(TAG, "isValidIpAddress() : e = " + e); return false; } numBlocks++; start = end + 1; end = value.indexOf('.', start); } return numBlocks == 4; } private boolean isIpDataInUiComplete() { ContentResolver contentResolver = getContentResolver(); for (int i = 0; i < (mPreferenceKeys.length - 1); i++) { EditTextPreference preference = (EditTextPreference) findPreference(mPreferenceKeys[i]); String text = preference.getText(); LOG("isIpDataInUiComplete() : text = " + text); if ( null == text || TextUtils.isEmpty(text) ) { return false; } } return true; } }
到这一步 Settings 的修改就完成了,就能实现上图的效果了,你可以mm push看效果了
如果你编译报错,大概是 Settings 中没有添加对应的变量,我的本来就有的,
没有的可参考下面的加一下
frameworks\base\core\java\android\provider\Settings.java
// Intent actions for Settings // ethernet public static final String ETHERNET_STATIC_IP = "ethernet_static_ip"; public static final String ETHERNET_STATIC_GATEWAY = "ethernet_static_gateway"; public static final String ETHERNET_STATIC_NETMASK = "ethernet_static_netmask"; public static final String ETHERNET_STATIC_DNS1 = "ethernet_static_dns1"; public static final String ETHERNET_STATIC_DNS2 = "ethernet_static_dns2"; public static final String IS_ETHERNET_OPEN = "isEthernetOpen"; public static final String IS_ETHERNET_STATUC_OPEN = "isEthernetStaticOpen";
加完后你需要先 make update-api成功后,在重新 mm 编译应该就好了
5、修改 framework 层网卡相关的控制代码
frameworks\opt\net\ethernet\java\com\android\server\ethernet\EthernetNetworkFactory.java
将原来的 startIpManager() 直接替换为如下的方法
public void startIpManager() { if (DBG) { Log.d(TAG, String.format("starting IpManager(%s): mNetworkInfo=%s", mIface, mNetworkInfo)); } LinkProperties linkProperties; IpConfiguration config = mEthernetManager.getConfiguration(); if (config.getIpAssignment() == IpAssignment.STATIC) { if (!setStaticIpAddress(config.getStaticIpConfiguration())) { // We've already logged an error. return; } linkProperties = config.getStaticIpConfiguration().toLinkProperties(mIface); if (config.getProxySettings() == ProxySettings.STATIC || config.getProxySettings() == ProxySettings.PAC) { // mIpManager.setHttpProxy(config.getHttpProxy()); linkProperties.setHttpProxy(config.getHttpProxy()); } String tcpBufferSizes = mContext.getResources().getString( com.android.internal.R.string.config_ethernet_tcp_buffers); if (TextUtils.isEmpty(tcpBufferSizes) == false) { linkProperties.setTcpBufferSizes(tcpBufferSizes); } } else { mNetworkInfo.setDetailedState(DetailedState.OBTAINING_IPADDR, null, mHwAddr); } IpManager.Callback ipmCallback = new IpManager.Callback() { @Override public void onProvisioningSuccess(LinkProperties newLp) { mHandler.post(() -> onIpLayerStarted(newLp)); } @Override public void onProvisioningFailure(LinkProperties newLp) { mHandler.post(() -> onIpLayerStopped(newLp)); } @Override public void onLinkPropertiesChange(LinkProperties newLp) { mHandler.post(() -> updateLinkProperties(newLp)); } }; stopIpManager(); mIpManager = new IpManager(mContext, mIface, ipmCallback); if (config.getProxySettings() == ProxySettings.STATIC || config.getProxySettings() == ProxySettings.PAC) { mIpManager.setHttpProxy(config.getHttpProxy()); } final String tcpBufferSizes = mContext.getResources().getString( com.android.internal.R.string.config_ethernet_tcp_buffers); if (!TextUtils.isEmpty(tcpBufferSizes)) { mIpManager.setTcpBufferSizes(tcpBufferSizes); } if (config.getIpAssignment() == IpAssignment.STATIC) { mIpManager.startProvisioning(config.getStaticIpConfiguration()); } else { final ProvisioningConfiguration provisioningConfiguration = mIpManager.buildProvisioningConfiguration() .withProvisioningTimeoutMs(0) .build(); mIpManager.startProvisioning(provisioningConfiguration); } }
frameworks\opt\net\ethernet\java\com\android\server\ethernet\EthernetServiceImpl.java
在 EthernetServiceImpl 中增加网卡开关和静态IP开关的监听处理
private final EthernetOpenedObserver mOpenObserver = new EthernetOpenedObserver(); private final EthernetStaticObserver mStaticObserver = new EthernetStaticObserver(); public EthernetServiceImpl(Context context) { ..... mContext.getContentResolver().registerContentObserver( System.getUriFor(IS_ETHERNET_OPEN), false, mOpenObserver); mContext.getContentResolver().registerContentObserver( System.getUriFor(ETHERNET_USE_STATIC_IP), false, mStaticObserver); } private void enforceChangePermission() { mContext.enforceCallingOrSelfPermission( android.Manifest.permission.CHANGE_NETWORK_STATE, "EthernetService"); } @Override public void setConfiguration(IpConfiguration config) { if (!mStarted.get()) { Log.w(TAG, "System isn't ready enough to change ethernet configuration"); } enforceChangePermission(); enforceConnectivityInternalPermission(); ..... } private boolean isStatic() { return Settings.System.getInt(mContext.getContentResolver(),ETHERNET_USE_STATIC_IP,0) ==1; } private int getState() { return Settings.System.getInt(mContext.getContentResolver(), IS_ETHERNET_OPEN,0); } private final class EthernetOpenedObserver extends ContentObserver { public EthernetOpenedObserver() { super(new Handler()); } @Override public void onChange(boolean selfChange, Uri uri, int userId) { super.onChange(selfChange, uri, userId); Log.i(TAG, "EthernetServiceImpl isEthernetOpen onChange...."); if (getState() == 1) { if (isStatic()) { StaticIpConfiguration staticIpConfiguration = new StaticIpConfiguration(); staticIpConfiguration.domains = Settings.System.getString(mContext.getContentResolver(),Settings.ETHERNET_STATIC_NETMASK); try { staticIpConfiguration.gateway = InetAddress.getByName(Settings.System.getString(mContext.getContentResolver(),Settings.ETHERNET_STATIC_GATEWAY)); staticIpConfiguration.ipAddress = new LinkAddress(InetAddress.getByName(Settings.System.getString(mContext.getContentResolver(),Settings.ETHERNET_STATIC_IP)), 24); staticIpConfiguration.dnsServers.add(InetAddress.getByName(Settings.System.getString(mContext.getContentResolver(),Settings.ETHERNET_STATIC_DNS1))); staticIpConfiguration.dnsServers.add(InetAddress.getByName(Settings.System.getString(mContext.getContentResolver(),Settings.ETHERNET_STATIC_DNS2))); } catch (Exception e){ e.printStackTrace(); } mIpConfiguration.staticIpConfiguration = staticIpConfiguration; } mStarted.set(true); mTracker.start(mContext, mHandler); } else { mTracker.stop(); } } } private final class EthernetStaticObserver extends ContentObserver { public EthernetStaticObserver() { super(new Handler()); } @Override public void onChange(boolean selfChange, Uri uri, int userId) { super.onChange(selfChange, uri, userId); Log.i(TAG, "EthernetServiceImpl isEthernetStaticOpen onChange...."); if (!isStatic()) { mIpConfiguration = mEthernetConfigStore.readIpAndProxyConfigurations(); mTracker.stop(); mStarted.set(true); mTracker.start(mContext, mHandler); } } }
好了,到此就搞定了动图的效果了,开关以太网和设置静态IP上网,下面我们再来瞅瞅 6.0的有啥区别
Android6.0
效果图和上面的一样,这里就省略了,Settings 的修改和上面的一样,有两个细小的
区别就是
1、导包需要注意,EthernetSettings 和 EthernetStaticIP 中的 Preference
相关的包,在8.1中都换成了 support.v7 下面的,6.0 还是用原来的 android.preference
2、6.0 中的 MetricsLogger.ETHERNET 和 MetricsLogger.ETHERNET_STATIC
在8.1中替换为 MetricsEvent.ETHERNET 和 MetricsEvent.ETHERNET_STATIC
导包 com.android.internal.logging.MetricsLogger
frameworks/base/proto/src/metrics_constants.proto
ETHERNET = 1144; ETHERNET_STATIC = 1145;
framework 方面的差异
EthernetServiceImpl.java 文件和 8.1 的一样,主要差别在 EthernetNetworkFactory.java
相比较而言没有 8.1 的复杂,下面贴一下完整的代码
frameworks\opt\net\ethernet\java\com\android\server\ethernet\EthernetNetworkFactory.java
class EthernetNetworkFactory { private static final String NETWORK_TYPE = "Ethernet"; private static final String TAG = "EthernetNetworkFactory"; private static final int NETWORK_SCORE = 110; private static final boolean DBG = true; public static final String ETHERNET_USE_STATIC_IP = Settings.IS_ETHERNET_STATUC_OPEN; /** Tracks interface changes. Called from NetworkManagementService. */ private InterfaceObserver mInterfaceObserver; /** For static IP configuration */ private EthernetManager mEthernetManager; /** To set link state and configure IP addresses. */ private INetworkManagementService mNMService; /* To communicate with ConnectivityManager */ private NetworkCapabilities mNetworkCapabilities; private NetworkAgent mNetworkAgent; private LocalNetworkFactory mFactory; private Context mContext; /** Product-dependent regular expression of interface names we track. */ private static String mIfaceMatch = ""; /** To notify Ethernet status. */ private final RemoteCallbackList<IEthernetServiceListener> mListeners; /** Data members. All accesses to these must be synchronized(this). */ private static String mIface = ""; private String mHwAddr; private static boolean mLinkUp; private NetworkInfo mNetworkInfo; private LinkProperties mLinkProperties; EthernetNetworkFactory(RemoteCallbackList<IEthernetServiceListener> listeners) { mNetworkInfo = new NetworkInfo(ConnectivityManager.TYPE_ETHERNET, 0, NETWORK_TYPE, ""); mLinkProperties = new LinkProperties(); initNetworkCapabilities(); mListeners = listeners; } private class LocalNetworkFactory extends NetworkFactory { LocalNetworkFactory(String name, Context context, Looper looper) { super(looper, context, name, new NetworkCapabilities()); } protected void startNetwork() { onRequestNetwork(); } protected void stopNetwork() { } } /** * Updates interface state variables. * Called on link state changes or on startup. */ private void updateInterfaceState(String iface, boolean up) { if (!mIface.equals(iface)) { return; } Log.d(TAG, "updateInterface: " + iface + " link " + (up ? "up" : "down")); synchronized(this) { mLinkUp = up; mNetworkInfo.setIsAvailable(up); if (!up) { // Tell the agent we're disconnected. It will call disconnect(). mNetworkInfo.setDetailedState(DetailedState.DISCONNECTED, null, mHwAddr); } updateAgent(); // set our score lower than any network could go // so we get dropped. TODO - just unregister the factory // when link goes down. mFactory.setScoreFilter(up ? NETWORK_SCORE : -1); } } private class InterfaceObserver extends BaseNetworkObserver { @Override public void interfaceLinkStateChanged(String iface, boolean up) { updateInterfaceState(iface, up); } @Override public void interfaceAdded(String iface) { maybeTrackInterface(iface); } @Override public void interfaceRemoved(String iface) { stopTrackingInterface(iface); } } private void setInterfaceUp(String iface) { // Bring up the interface so we get link status indications. try { NetworkUtils.stopDhcp(iface); mNMService.setInterfaceUp(iface); String hwAddr = null; InterfaceConfiguration config = mNMService.getInterfaceConfig(iface); if (config == null) { Log.e(TAG, "Null iterface config for " + iface + ". Bailing out."); return; } synchronized (this) { if (!isTrackingInterface()) { setInterfaceInfoLocked(iface, config.getHardwareAddress()); mNetworkInfo.setIsAvailable(true); mNetworkInfo.setExtraInfo(mHwAddr); } else { Log.e(TAG, "Interface unexpectedly changed from " + iface + " to " + mIface); mNMService.setInterfaceDown(iface); } } } catch (RemoteException e) { Log.e(TAG, "Error upping interface " + mIface + ": " + e); } } private boolean maybeTrackInterface(String iface) { // If we don't already have an interface, and if this interface matches // our regex, start tracking it. if (!iface.matches(mIfaceMatch) || isTrackingInterface()) return false; Log.d(TAG, "Started tracking interface " + iface); setInterfaceUp(iface); return true; } private void stopTrackingInterface(String iface) { if (!iface.equals(mIface)) return; Log.d(TAG, "Stopped tracking interface " + iface); // TODO: Unify this codepath with stop(). synchronized (this) { NetworkUtils.stopDhcp(mIface); setInterfaceInfoLocked("", null); mNetworkInfo.setExtraInfo(null); mLinkUp = false; mNetworkInfo.setDetailedState(DetailedState.DISCONNECTED, null, mHwAddr); updateAgent(); mNetworkAgent = null; mNetworkInfo = new NetworkInfo(ConnectivityManager.TYPE_ETHERNET, 0, NETWORK_TYPE, ""); mLinkProperties = new LinkProperties(); } } private boolean setStaticIpAddress(StaticIpConfiguration staticConfig) { if (staticConfig!=null && staticConfig.ipAddress != null && staticConfig.gateway != null && staticConfig.dnsServers.size() > 0) { try { Log.i(TAG, "Applying static IPv4 configuration to " + mIface + ": " + staticConfig); InterfaceConfiguration config = mNMService.getInterfaceConfig(mIface); config.setLinkAddress(staticConfig.ipAddress); mNMService.setInterfaceConfig(mIface, config); return true; } catch(RemoteException|IllegalStateException e) { Log.e(TAG, "Setting static IP address failed: " + e.getMessage()); } } else { Log.e(TAG, "Invalid static IP configuration."); } return false; } public void updateAgent() { synchronized (EthernetNetworkFactory.this) { if (mNetworkAgent == null) return; if (DBG) { Log.i(TAG, "Updating mNetworkAgent with: " + mNetworkCapabilities + ", " + mNetworkInfo + ", " + mLinkProperties); } mNetworkAgent.sendNetworkCapabilities(mNetworkCapabilities); mNetworkAgent.sendNetworkInfo(mNetworkInfo); mNetworkAgent.sendLinkProperties(mLinkProperties); // never set the network score below 0. mNetworkAgent.sendNetworkScore(mLinkUp? NETWORK_SCORE : 0); } } /* Called by the NetworkFactory on the handler thread. */ public void onRequestNetwork() { // TODO: Handle DHCP renew. Thread dhcpThread = new Thread(new Runnable() { public void run() { if (DBG) Log.i(TAG, "dhcpThread(" + mIface + "): mNetworkInfo=" + mNetworkInfo); LinkProperties linkProperties; IpConfiguration config = mEthernetManager.getConfiguration(); /* if (config.getIpAssignment() == IpAssignment.STATIC) */ if (isStatic()) { if (!setStaticIpAddress(config.getStaticIpConfiguration())) { // We've already logged an error. return; } linkProperties = config.getStaticIpConfiguration().toLinkProperties(mIface); } else { mNetworkInfo.setDetailedState(DetailedState.OBTAINING_IPADDR, null, mHwAddr); DhcpResults dhcpResults = new DhcpResults(); // TODO: Handle DHCP renewals better. // In general runDhcp handles DHCP renewals for us, because // the dhcp client stays running, but if the renewal fails, // we will lose our IP address and connectivity without // noticing. if (!NetworkUtils.runDhcp(mIface, dhcpResults)) { Log.e(TAG, "DHCP request error:" + NetworkUtils.getDhcpError()); // set our score lower than any network could go // so we get dropped. mFactory.setScoreFilter(-1); // If DHCP timed out (as opposed to failing), the DHCP client will still be // running, because in M we changed its timeout to infinite. Stop it now. NetworkUtils.stopDhcp(mIface); return; } linkProperties = dhcpResults.toLinkProperties(mIface); } if (config.getProxySettings() == ProxySettings.STATIC || config.getProxySettings() == ProxySettings.PAC) { linkProperties.setHttpProxy(config.getHttpProxy()); } String tcpBufferSizes = mContext.getResources().getString( com.android.internal.R.string.config_ethernet_tcp_buffers); if (TextUtils.isEmpty(tcpBufferSizes) == false) { linkProperties.setTcpBufferSizes(tcpBufferSizes); } synchronized(EthernetNetworkFactory.this) { if (mNetworkAgent != null) { Log.e(TAG, "Already have a NetworkAgent - aborting new request"); return; } mLinkProperties = linkProperties; mNetworkInfo.setIsAvailable(true); mNetworkInfo.setDetailedState(DetailedState.CONNECTED, null, mHwAddr); // Create our NetworkAgent. mNetworkAgent = new NetworkAgent(mFactory.getLooper(), mContext, NETWORK_TYPE, mNetworkInfo, mNetworkCapabilities, mLinkProperties, NETWORK_SCORE) { public void unwanted() { synchronized(EthernetNetworkFactory.this) { if (this == mNetworkAgent) { NetworkUtils.stopDhcp(mIface); mLinkProperties.clear(); mNetworkInfo.setDetailedState(DetailedState.DISCONNECTED, null, mHwAddr); updateAgent(); mNetworkAgent = null; try { mNMService.clearInterfaceAddresses(mIface); } catch (Exception e) { Log.e(TAG, "Failed to clear addresses or disable ipv6" + e); } } else { Log.d(TAG, "Ignoring unwanted as we have a more modern " + "instance"); } } }; }; } } }); dhcpThread.start(); } /** * Begin monitoring connectivity */ public synchronized void start(Context context, Handler target) { // The services we use. IBinder b = ServiceManager.getService(Context.NETWORKMANAGEMENT_SERVICE); mNMService = INetworkManagementService.Stub.asInterface(b); mEthernetManager = (EthernetManager) context.getSystemService(Context.ETHERNET_SERVICE); // Interface match regex. mIfaceMatch = context.getResources().getString( com.android.internal.R.string.config_ethernet_iface_regex); // Create and register our NetworkFactory. mFactory = new LocalNetworkFactory(NETWORK_TYPE, context, target.getLooper()); mFactory.setCapabilityFilter(mNetworkCapabilities); mFactory.setScoreFilter(-1); // this set high when we have an iface mFactory.register(); mContext = context; // Start tracking interface change events. mInterfaceObserver = new InterfaceObserver(); try { mNMService.registerObserver(mInterfaceObserver); } catch (RemoteException e) { Log.e(TAG, "Could not register InterfaceObserver " + e); } // If an Ethernet interface is already connected, start tracking that. // Otherwise, the first Ethernet interface to appear will be tracked. try { final String[] ifaces = mNMService.listInterfaces(); for (String iface : ifaces) { synchronized(this) { if (maybeTrackInterface(iface)) { // We have our interface. Track it. // Note: if the interface already has link (e.g., if we // crashed and got restarted while it was running), // we need to fake a link up notification so we start // configuring it. Since we're already holding the lock, // any real link up/down notification will only arrive // after we've done this. if (mNMService.getInterfaceConfig(iface).hasFlag("running")) { updateInterfaceState(iface, true); } break; } } } } catch (RemoteException e) { Log.e(TAG, "Could not get list of interfaces " + e); } } public synchronized void stop() { NetworkUtils.stopDhcp(mIface); // ConnectivityService will only forget our NetworkAgent if we send it a NetworkInfo object // with a state of DISCONNECTED or SUSPENDED. So we can't simply clear our NetworkInfo here: // that sets the state to IDLE, and ConnectivityService will still think we're connected. // // TODO: stop using explicit comparisons to DISCONNECTED / SUSPENDED in ConnectivityService, // and instead use isConnectedOrConnecting(). mNetworkInfo.setDetailedState(DetailedState.DISCONNECTED, null, mHwAddr); mLinkUp = false; updateAgent(); mLinkProperties = new LinkProperties(); mNetworkAgent = null; setInterfaceInfoLocked("", null); mNetworkInfo = new NetworkInfo(ConnectivityManager.TYPE_ETHERNET, 0, NETWORK_TYPE, ""); mFactory.unregister(); } private void initNetworkCapabilities() { mNetworkCapabilities = new NetworkCapabilities(); mNetworkCapabilities.addTransportType(NetworkCapabilities.TRANSPORT_ETHERNET); mNetworkCapabilities.addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET); mNetworkCapabilities.addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED); // We have no useful data on bandwidth. Say 100M up and 100M down. :-( mNetworkCapabilities.setLinkUpstreamBandwidthKbps(100 * 1000); mNetworkCapabilities.setLinkDownstreamBandwidthKbps(100 * 1000); } public synchronized boolean isTrackingInterface() { return !TextUtils.isEmpty(mIface); } /** * Set interface information and notify listeners if availability is changed. * This should be called with the lock held. */ private void setInterfaceInfoLocked(String iface, String hwAddr) { boolean oldAvailable = isTrackingInterface(); mIface = iface; mHwAddr = hwAddr; boolean available = isTrackingInterface(); if (oldAvailable != available) { int n = mListeners.beginBroadcast(); for (int i = 0; i < n; i++) { try { mListeners.getBroadcastItem(i).onAvailabilityChanged(available); } catch (RemoteException e) { // Do nothing here. } } mListeners.finishBroadcast(); } } synchronized void dump(FileDescriptor fd, IndentingPrintWriter pw, String[] args) { if (isTrackingInterface()) { pw.println("Tracking interface: " + mIface); pw.increaseIndent(); pw.println("MAC address: " + mHwAddr); pw.println("Link state: " + (mLinkUp ? "up" : "down")); pw.decreaseIndent(); } else { pw.println("Not tracking any interface"); } pw.println(); pw.println("NetworkInfo: " + mNetworkInfo); pw.println("LinkProperties: " + mLinkProperties); pw.println("NetworkAgent: " + mNetworkAgent); } /*private boolean isStatic()*/ private boolean isStatic() { return Settings.System.getInt(mContext.getContentResolver(),ETHERNET_USE_STATIC_IP,0) ==1; } }