Android P 9.0 MTK平台 增加以太网静态IP功能

2024-08-22 06:32

本文主要是介绍Android P 9.0 MTK平台 增加以太网静态IP功能,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

前言

朋友们,最近又开始搞 Android P了,同样的以太网静态 IP 是少不了的功能,今天我们就开始来整一下。之前弄6.0 和 8.1 的都 ok 了。

没想到 9.0 改动还是略微有点大的。来来来,先看图。

效果图

在这里插入图片描述

上代码

app层

Settings 中的代码和之前的一样就不贴了,可以去看之前的这篇中代码 Android8.1 MTK平台 增加以太网静态IP功能

或者下载这篇的源码资源

framework 层

驱动大哥已经把驱动都搞定了,现在直接插上网线,设备就能上网,网卡图标也正常显示。我们需要控制网卡的开关,先来简单看下流程。

frameworks\opt\net\ethernet\java\com\android\server\ethernet\EthernetService.java

public final class EthernetService extends SystemService {private static final String TAG = "EthernetService";final EthernetServiceImpl mImpl;public EthernetService(Context context) {super(context);mImpl = new EthernetServiceImpl(context);}@Overridepublic void onStart() {Log.i(TAG, "Registering service " + Context.ETHERNET_SERVICE);publishBinderService(Context.ETHERNET_SERVICE, mImpl);}@Overridepublic void onBootPhase(int phase) {if (phase == SystemService.PHASE_SYSTEM_SERVICES_READY) {mImpl.start();}}
}

EthernetService 继承了系统服务,那自然也就是系统服务,如果挂掉会自动重新创建,严重情况会导致系统重启,我就试出来过。看下当 PHASE_SYSTEM_SERVICES_READY 准备完成,调用 EthernetServiceImpl 的 start()

来看下这个 start() 都干了啥

frameworks\opt\net\ethernet\java\com\android\server\ethernet\EthernetServiceImpl.java

    public void start() {Log.i(TAG, "Starting Ethernet service");HandlerThread handlerThread = new HandlerThread("EthernetServiceThread");handlerThread.start();mHandler = new Handler(handlerThread.getLooper());mTracker = new EthernetTracker(mContext, mHandler);mTracker.start();mStarted.set(true);
}

主要创建了 EthernetTracker,这个类是 9.0 中新增出来的,用于监听以太网的切换、以太网判断当前网络是否可用等一系列操作。之前 8.1 中都集成在 EthernetNetworkFactory 中,继续跟进

frameworks\opt\net\ethernet\java\com\android\server\ethernet\EthernetTracker.java

void start() {mConfigStore.read();// Default interface is just the first one we want to track.mIpConfigForDefaultInterface = mConfigStore.getIpConfigurationForDefaultInterface();final ArrayMap<String, IpConfiguration> configs = mConfigStore.getIpConfigurations();Log.e(TAG, "mIpConfigForDefaultInterface== " + mIpConfigForDefaultInterface);Log.i(TAG, "IpConfiguration size== " + configs.size());for (int i = 0; i < configs.size(); i++) {mIpConfigurations.put(configs.keyAt(i), configs.valueAt(i));}try {mNMService.registerObserver(new InterfaceObserver());} catch (RemoteException e) {Log.e(TAG, "Could not register InterfaceObserver " + e);}mHandler.post(this::trackAvailableInterfaces);}

mConfigStore 对象用来管理保存的 IpConfigStore 信息,EthernetConfigStore 中通过读取 /misc/ethernet/ipconfig.txt 中保存的信息进行维护一个 ArrayMap<String, IpConfiguration>,根据打印的日志看,start() 中每次获取到的 size 都为 0,基本上没起作用。值得一提的是,在 EthernetTracker 的构造方法中通过解析 config_ethernet_interfaces 字符串也可向 map 中添加初始信息。

EthernetTracker(Context context, Handler handler) {mHandler = handler;// The services we use.IBinder b = ServiceManager.getService(Context.NETWORKMANAGEMENT_SERVICE);mNMService = INetworkManagementService.Stub.asInterface(b);// Interface match regex.mIfaceMatch = context.getResources().getString(com.android.internal.R.string.config_ethernet_iface_regex);// Read default Ethernet interface configuration from resourcesfinal String[] interfaceConfigs = context.getResources().getStringArray(com.android.internal.R.array.config_ethernet_interfaces);for (String strConfig : interfaceConfigs) {parseEthernetConfig(strConfig);}mConfigStore = new EthernetConfigStore();NetworkCapabilities nc = createNetworkCapabilities(true /* clear default capabilities */);mFactory = new EthernetNetworkFactory(handler, context, nc);mFactory.register();}private void parseEthernetConfig(String configString) {String[] tokens = configString.split(";");String name = tokens[0];String capabilities = tokens.length > 1 ? tokens[1] : null;NetworkCapabilities nc = createNetworkCapabilities(!TextUtils.isEmpty(capabilities)  /* clear default capabilities */, capabilities);mNetworkCapabilities.put(name, nc);if (tokens.length > 2 && !TextUtils.isEmpty(tokens[2])) {IpConfiguration ipConfig = parseStaticIpConfiguration(tokens[2]);mIpConfigurations.put(name, ipConfig);}}

config_ethernet_interfaces 的初始值位置在

frameworks\base\core\res\res\values\config.xml

<!-- Regex of wired ethernet ifaces --><string translatable="false" name="config_ethernet_iface_regex">eth\\d</string><!-- Configuration of Ethernet interfaces in the following format:<interface name|mac address>;[Network Capabilities];[IP config]Where[Network Capabilities] Optional. A comma seprated list of network capabilities.Values must be from NetworkCapabilities#NET_CAPABILITIES_* constants.[IP config] Optional. If empty or not specified - DHCP will be used, otherwiseuse the following format to specify static IP configuration:ip=<ip-address/mask> gateway=<ip-address> dns=<comma-sep-ip-addresses>domains=<comma-sep-domains> --><string-array translatable="false" name="config_ethernet_interfaces"><!--<item>eth1;12,13,14,15;ip=192.168.0.10/24 gateway=192.168.0.1 dns=4.4.4.4,8.8.8.8</item><item>eth2;;ip=192.168.0.11/24</item>--></string-array>

好了继续回到刚刚的 start() 中,接下来应该调用 trackAvailableInterfaces(),来看下完整的流程,maybeTrackInterface 中先进行 iface 判断,这个指代要使用的网口名称,通过命令 ifconfig -a 可以看到你设备下的所有网口名称。

KlxJ6e.png

mIfaceMatch 对应刚刚的 config.xml 中配置的 eth\d, 所以只有是 eth 打头并且 mFactory 中不存在的才能设置以太网连接,这很关键

继续调用 addInterface(), 将 iface 对应的 ipConfiguration 通过 mFactory addInterface,再然后 updateInterfaceState 广播通知当前以太网连接状态 CONNECTED/CONNECTED

private void trackAvailableInterfaces() {try {final String[] ifaces = mNMService.listInterfaces();for (String iface : ifaces) {maybeTrackInterface(iface);}} catch (RemoteException | IllegalStateException e) {Log.e(TAG, "Could not get list of interfaces " + e);}}private void maybeTrackInterface(String iface) {if (DBG) Log.i(TAG, "maybeTrackInterface " + iface);// If we don't already track this interface, and if this interface matches// our regex, start tracking it.if (!iface.matches(mIfaceMatch) || mFactory.hasInterface(iface)) {Log.d(TAG, iface + "  return ");return;}Log.e(TAG, "maybeTrackInterface " + iface);if (mIpConfigForDefaultInterface != null) {updateIpConfiguration(iface, mIpConfigForDefaultInterface);mIpConfigForDefaultInterface = null;}addInterface(iface);}private void addInterface(String iface) {InterfaceConfiguration config = null;// Bring up the interface so we get link status indications.try {mNMService.setInterfaceUp(iface);config = mNMService.getInterfaceConfig(iface);} catch (RemoteException | IllegalStateException e) {// Either the system is crashing or the interface has disappeared. Just ignore the// error; we haven't modified any state because we only do that if our calls succeed.Log.e(TAG, "Error upping interface " + iface, e);}if (config == null) {Log.e(TAG, "Null interface config for " + iface + ". Bailing out.");return;}final String hwAddress = config.getHardwareAddress();NetworkCapabilities nc = mNetworkCapabilities.get(iface);if (nc == null) {// Try to resolve using mac addressnc = mNetworkCapabilities.get(hwAddress);if (nc == null) {nc = createDefaultNetworkCapabilities();}}IpConfiguration ipConfiguration = mIpConfigurations.get(iface);if (ipConfiguration == null) {ipConfiguration = createDefaultIpConfiguration();}Log.d(TAG, "Started tracking interface " + iface);mFactory.addInterface(iface, hwAddress, nc, ipConfiguration);// 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.if (config.hasFlag("running")) {updateInterfaceState(iface, true);}}private void updateInterfaceState(String iface, boolean up) {Log.e(TAG, "updateInterfaceState up==" + up);boolean modified = mFactory.updateInterfaceLinkState(iface, up);if (modified) {boolean restricted = isRestrictedInterface(iface);int n = mListeners.beginBroadcast();for (int i = 0; i < n; i++) {try {if (restricted) {ListenerInfo listenerInfo = (ListenerInfo) mListeners.getBroadcastCookie(i);if (!listenerInfo.canUseRestrictedNetworks) {continue;}}mListeners.getBroadcastItem(i).onAvailabilityChanged(iface, up);} catch (RemoteException e) {// Do nothing here.}}mListeners.finishBroadcast();}}

Kl6fv4.png

知道了 EthernetTracker 的 start() 去连接以太网,那么我们在 EthernetServiceImpl 中增加布尔判断就能控制以太网开关状态。

frameworks\opt\net\ethernet\java\com\android\server\ethernet\EthernetServiceImpl.java

public class EthernetServiceImpl extends IEthernetManager.Stub {private static final String TAG = "EthernetServiceImpl";public static final String IS_ETHERNET_OPEN = Settings.IS_ETHERNET_OPEN;public static final String ETHERNET_USE_STATIC_IP = Settings.IS_ETHERNET_STATUC_OPEN; private final Context mContext;private final AtomicBoolean mStarted = new AtomicBoolean(false);private IpConfiguration mIpConfiguration;private final EthernetOpenedObserver mOpenObserver = new EthernetOpenedObserver();private final EthernetStaticObserver mStaticObserver = new EthernetStaticObserver(); private Handler mHandler;private EthernetTracker mTracker;public EthernetServiceImpl(Context context) {mContext = context;Log.i(TAG, "Creating EthernetConfigStore");mContext.getContentResolver().registerContentObserver(System.getUriFor(IS_ETHERNET_OPEN), false, mOpenObserver);mContext.getContentResolver().registerContentObserver(System.getUriFor(ETHERNET_USE_STATIC_IP), false, mStaticObserver); }....public void start() {Log.i(TAG, "Starting Ethernet service");HandlerThread handlerThread = new HandlerThread("EthernetServiceThread");handlerThread.start();mHandler = new Handler(handlerThread.getLooper());mTracker = new EthernetTracker(mContext, mHandler);mIpConfiguration = mTracker.getDefaultIpConfiguration();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.ipAssignment = IpAssignment.STATIC;mIpConfiguration.proxySettings = ProxySettings.STATIC;mIpConfiguration.staticIpConfiguration = staticIpConfiguration;}mTracker.start();mStarted.set(true);}  }private boolean isStatic(){Log.e(TAG, "EthernetServiceImpl isStatic()  " + Settings.System.getInt(mContext.getContentResolver(),ETHERNET_USE_STATIC_IP,0));return Settings.System.getInt(mContext.getContentResolver(),ETHERNET_USE_STATIC_IP,0) ==1;}   private int getState(){int state = Settings.System.getInt(mContext.getContentResolver(), IS_ETHERNET_OPEN,0);Log.e(TAG, "EthernetServiceImpl getState()  " + state);return state;}....@Overridepublic void setConfiguration(String iface, IpConfiguration config) {if (!mStarted.get()) {Log.w(TAG, "System isn't ready enough to change ethernet configuration");}enforceConnectivityInternalPermission();if (mTracker.isRestrictedInterface(iface)) {enforceUseRestrictedNetworksPermission();}Log.e(TAG, "setConfiguration iface="+iface);// TODO: this does not check proxy settings, gateways, etc.// Fix this by making IpConfiguration a complete representation of static configuration.mTracker.updateIpConfiguration(iface, new IpConfiguration(config));//addmTracker.removeInterface(iface);mTracker.start();}....private final class EthernetOpenedObserver extends ContentObserver {public EthernetOpenedObserver() {super(new Handler());}@Overridepublic 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.ipAssignment = IpAssignment.STATIC;mIpConfiguration.proxySettings = ProxySettings.STATIC;mIpConfiguration.staticIpConfiguration = staticIpConfiguration;}mTracker.start();mStarted.set(true);}else {    mTracker.stop();        }}}private final class EthernetStaticObserver extends ContentObserver {public EthernetStaticObserver() {super(new Handler());}@Overridepublic void onChange(boolean selfChange, Uri uri, int userId) {super.onChange(selfChange, uri, userId);Log.i(TAG, "EthernetServiceImpl isEthernetStaticOpen onChange....");if (!isStatic()) {Log.e(TAG, " no static stop and start");mTracker.recoverDHCPIpConfiguration();mTracker.stop();mTracker.start();           mStarted.set(true);}  }}

根据 settings 中设置的值 IS_ETHERNET_OPEN 和 ETHERNET_USE_STATIC_IP 判断是否加载,在 EthernetTracker 中新增如下几个方法

frameworks\opt\net\ethernet\java\com\android\server\ethernet\EthernetTracker.java

//关闭网卡,先更新状态为 false, 再从 mFactory 中移除 eth0, 不然关闭后无法再次打开,因为上面提到的判断
public void stop() {Log.d(TAG, "EthernetTracker stop ethernet...");updateInterfaceState("eth0", false);android.os.SystemClock.sleep(200);removeInterface("eth0");}//获取默认的 IpConfiguration,如果不存在则新建一个 DHCP 类型的,根据实际情况修改 ipAssignment 和 proxySettings
public IpConfiguration getDefaultIpConfiguration(){IpConfiguration ipConfiguration = mIpConfigurations.get("eth0");return ipConfiguration != null ? ipConfiguration : new IpConfiguration(IpAssignment.DHCP, ProxySettings.NONE, null, null);
}//从静态 IP 切换
public void recoverDHCPIpConfiguration(){mIpConfigurations.put("eth0", new IpConfiguration(IpAssignment.DHCP, ProxySettings.NONE, null, null));
}

测试发现频繁点击 静态IP 开关时,出现了数组角标越界的情况,应该是 add 和 remove iface导致的,直接将打印 try 一下就可以

2019-10-21 17:01:38.675 1075-1285/? E/AndroidRuntime: *** FATAL EXCEPTION IN SYSTEM PROCESS: EthernetServiceThreadjava.lang.ArrayIndexOutOfBoundsException: length=2; index=-1at com.android.internal.util.StateMachine$SmHandler.getCurrentState(StateMachine.java:1151)at com.android.internal.util.StateMachine$SmHandler.access$1300(StateMachine.java:681)at com.android.internal.util.StateMachine.toString(StateMachine.java:2088)at java.lang.String.valueOf(String.java:2896)at java.lang.StringBuilder.append(StringBuilder.java:132)at com.android.server.ethernet.EthernetNetworkFactory$NetworkInterfaceState.toString(EthernetNetworkFactory.java:422)at java.lang.String.valueOf(String.java:2896)at java.lang.StringBuilder.append(StringBuilder.java:132)at com.android.server.ethernet.EthernetNetworkFactory.networkForRequest(EthernetNetworkFactory.java:213)at com.android.server.ethernet.EthernetNetworkFactory.acceptRequest(EthernetNetworkFactory.java:78)at android.net.NetworkFactory.evalRequest(NetworkFactory.java:234)at android.net.NetworkFactory.evalRequests(NetworkFactory.java:253)at android.net.NetworkFactory.handleSetFilter(NetworkFactory.java:204)at android.net.NetworkFactory.handleMessage(NetworkFactory.java:149)at android.os.Handler.dispatchMessage(Handler.java:106)at android.os.Looper.loop(Looper.java:193)at android.os.HandlerThread.run(HandlerThread.java:65)

frameworks\opt\net\ethernet\java\com\android\server\ethernet\EthernetNetworkFactory.java

@Overridepublic String toString() {try{return getClass().getSimpleName() + "{ "+ "iface: " + name + ", "+ "up: " + mLinkUp + ", "+ "hwAddress: " + mHwAddress + ", "+ "networkInfo: " + mNetworkInfo + ", "+ "networkAgent: " + mNetworkAgent + ", "+ "ipClient: " + mIpClient + ","+ "linkProperties: " + mLinkProperties+ "}";}catch(Exception e){e.printStackTrace();return getClass().getSimpleName() + "{ }";}}

这样就能实现开头的动图效果了

修改文件源码下载

android 9.0 添加Ethernet功能(settings+framework).zip

这篇关于Android P 9.0 MTK平台 增加以太网静态IP功能的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1095502

相关文章

使用EasyPoi快速导出Word文档功能的实现步骤

《使用EasyPoi快速导出Word文档功能的实现步骤》EasyPoi是一个基于ApachePOI的开源Java工具库,旨在简化Excel和Word文档的操作,本文将详细介绍如何使用EasyPoi快速... 目录一、准备工作1、引入依赖二、准备好一个word模版文件三、编写导出方法的工具类四、在Export

JS纯前端实现浏览器语音播报、朗读功能的完整代码

《JS纯前端实现浏览器语音播报、朗读功能的完整代码》在现代互联网的发展中,语音技术正逐渐成为改变用户体验的重要一环,下面:本文主要介绍JS纯前端实现浏览器语音播报、朗读功能的相关资料,文中通过代码... 目录一、朗读单条文本:① 语音自选参数,按钮控制语音:② 效果图:二、朗读多条文本:① 语音有默认值:②

C#实现高性能拍照与水印添加功能完整方案

《C#实现高性能拍照与水印添加功能完整方案》在工业检测、质量追溯等应用场景中,经常需要对产品进行拍照并添加相关信息水印,本文将详细介绍如何使用C#实现一个高性能的拍照和水印添加功能,包含完整的代码实现... 目录1. 概述2. 功能架构设计3. 核心代码实现python3.1 主拍照方法3.2 安全HBIT

基于Go语言开发一个 IP 归属地查询接口工具

《基于Go语言开发一个IP归属地查询接口工具》在日常开发中,IP地址归属地查询是一个常见需求,本文将带大家使用Go语言快速开发一个IP归属地查询接口服务,有需要的小伙伴可以了解下... 目录功能目标技术栈项目结构核心代码(main.go)使用方法扩展功能总结在日常开发中,IP 地址归属地查询是一个常见需求:

录音功能在哪里? 电脑手机等设备打开录音功能的技巧

《录音功能在哪里?电脑手机等设备打开录音功能的技巧》很多时候我们需要使用录音功能,电脑和手机这些常用设备怎么使用录音功能呢?下面我们就来看看详细的教程... 我们在会议讨论、采访记录、课堂学习、灵感创作、法律取证、重要对话时,都可能有录音需求,便于留存关键信息。下面分享一下如何在电脑端和手机端上找到录音功能

Android实现图片浏览功能的示例详解(附带源码)

《Android实现图片浏览功能的示例详解(附带源码)》在许多应用中,都需要展示图片并支持用户进行浏览,本文主要为大家介绍了如何通过Android实现图片浏览功能,感兴趣的小伙伴可以跟随小编一起学习一... 目录一、项目背景详细介绍二、项目需求详细介绍三、相关技术详细介绍四、实现思路详细介绍五、完整实现代码

在Android中使用WebView在线查看PDF文件的方法示例

《在Android中使用WebView在线查看PDF文件的方法示例》在Android应用开发中,有时我们需要在客户端展示PDF文件,以便用户可以阅读或交互,:本文主要介绍在Android中使用We... 目录简介:1. WebView组件介绍2. 在androidManifest.XML中添加Interne

SpringBoot中@Value注入静态变量方式

《SpringBoot中@Value注入静态变量方式》SpringBoot中静态变量无法直接用@Value注入,需通过setter方法,@Value(${})从属性文件获取值,@Value(#{})用... 目录项目场景解决方案注解说明1、@Value("${}")使用示例2、@Value("#{}"php

Python使用FastAPI实现大文件分片上传与断点续传功能

《Python使用FastAPI实现大文件分片上传与断点续传功能》大文件直传常遇到超时、网络抖动失败、失败后只能重传的问题,分片上传+断点续传可以把大文件拆成若干小块逐个上传,并在中断后从已完成分片继... 目录一、接口设计二、服务端实现(FastAPI)2.1 运行环境2.2 目录结构建议2.3 serv

Debian 13升级后网络转发等功能异常怎么办? 并非错误而是管理机制变更

《Debian13升级后网络转发等功能异常怎么办?并非错误而是管理机制变更》很多朋友反馈,更新到Debian13后网络转发等功能异常,这并非BUG而是Debian13Trixie调整... 日前 Debian 13 Trixie 发布后已经有众多网友升级到新版本,只不过升级后发现某些功能存在异常,例如网络转