实现WIFI白名单功能
前言
针对定制案子有客户需求是要有一个WIFI白名单功能
WIFI白名单解释-需求理解: 设置
- 客户指定的SSID 才能够在WIFI列表中显示出来;
- 默认情况下所有WIFI列表可以显示
- 客户可以编辑白名单SSID,实现白名单可控
如下,应用界面需求:
一、参考资料
具体需要大家自己实验,实际会遇到各种问题,参考资料只是给一个思路而已。
按照上面思路,可以实现Framework 层思路,具体还会遇到如下问题,这里给一个参考,实际需要自己解决。
- 源码继承后无法编译,方法存在错误,需要自己修复,对齐
- 无法编译,修改了系统的api,修改了aidl 文件和service 里面新增了对应的方法,需要更新系统编译的api .
make update-api
二、涉及修改的文件
/frameworks/base/wifi/java/android/net/wifi/IWifiManager.aidl
/frameworks/base/wifi/java/android/net/wifi/WifiManager.java
/frameworks/opt/net/wifi/service/java/com/android/server/wifi/BaseWifiService.java
/frameworks/opt/net/wifi/service/java/com/android/server/wifi/WifiServiceImpl.java
实现思路
初看需求的时候,其实还不知道实现方案的,查阅资料修改起来也是云里雾里。 功能实现后,我发现思路其实特别重要。 如上需求已经很明确了,如何实现呢? 这里的思路其实就是:WIFI 获取WIFI列表这个步骤、方法中进行拦截。 默认情况下返回所有的WIFI列表、如果白名单不为空则在所有WIFI列表和白名单列表进行匹配,只返回白名单里面的且实际扫码到的WIFI列表页存在的 SSID 才会显示出来。
三、源码文件修改
AIDL文件IWifiManager.aidl 新增方法
路径:/frameworks/base/wifi/java/android/net/wifi/IWifiManager.aidl
修改内容如下,添加设置和获取白名单方法,新增的方法
/**
* Interface that allows controlling and querying Wi-Fi connectivity.
*
* {@hide}
*/
interface IWifiManager
{
。。。。。。。。。。
void setWiFiWhiteList(in List<String> whiteList);
List<String> getWiFiWhiteList();
}
服务 WifiManager.java 实现方法
路径:/frameworks/base/wifi/java/android/net/wifi/WifiManager.java
修改内容如下,实现aidl 文件中添加的方法
@SystemService(Context.WIFI_SERVICE)
public class WifiManager {
。。。。。。。。。。
@NonNull
public void setWiFiWhiteList(@Nullable List<String> blackList){
try {
Log.v(TAG," setWiFiWhiteList ");
mService.setWiFiWhiteList(blackList);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
@NonNull
public List<String> getWiFiWhiteList(){
try {
Log.v(TAG," getWiFiWhiteList ");
return mService.getWiFiWhiteList();
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
}
备注:这里的方法可不是 overide 方法,这里不做延伸说明 。
看看 我们在这里其实是一个服务,这个Service 里面最终调用的是 mService 方法,这个mService 变量是什么? 且看如下:它其实就是一个Binder
AIDL实现类 BaseWifiService.java 新增实现方法
路径:/frameworks/opt/net/wifi/service/java/com/android/server/wifi/BaseWifiService.java
修改内容如下,实现添加设置和获取白名单方法,新增的方法
/**
* Empty concrete class implementing IWifiManager with stub methods throwing runtime exceptions.
*
* This class is meant to be extended by real implementations of IWifiManager in order to facilitate
* cross-repo changes to WiFi internal APIs, including the introduction of new APIs, the removal of
* deprecated APIs, or the migration of existing API signatures.
*
* When an existing API is scheduled for removal, it can be removed from IWifiManager.aidl
* immediately and marked as @Deprecated first in this class. Children inheriting this class are
* then given a short grace period to update themselves before the @Deprecated stub is removed for
* good. If the API scheduled for removal has a replacement or an overload (signature change),
* these should be introduced before the stub is removed to allow children to migrate.
*
* When a new API is added to IWifiManager.aidl, a stub should be added in BaseWifiService as
* well otherwise compilation will fail.
*/
public class BaseWifiService extends IWifiManager.Stub {
private static final String TAG = BaseWifiService.class.getSimpleName();
。。。。。。。。。。。。。。。。。。。。
@Override
public void setWiFiWhiteList( List<String> blackList){
throw new UnsupportedOperationException();
};
@Override
public List<String> getWiFiWhiteList(){
throw new UnsupportedOperationException();
};
}
这里看注释其实就一目了然了,知道在这里要实现aidl 接口方法
WifiServiceImpl.java 继承 BaseWifiService
路径:/frameworks/opt/net/wifi/service/java/com/android/server/wifi/WifiServiceImpl.java
在 父类BaseWifiService 已经讲了,它是aidl对应的实现类, 这里WifiServiceImpl 作为子类,是功能的补充或者实现吧,处理逻辑和业务。
我们看看类声明,其实很有感觉的,如下:处理remote 端wifi 的操作请求,通过实现IWifiManager 接口。
/**
* WifiService handles remote WiFi operation requests by implementing
* the IWifiManager interface.
*/
public class WifiServiceImpl extends BaseWifiService {
private static final String TAG = "WifiServiceImpl";
这里我们重点关注的 getScanResults 请求wifi 扫码列表就在这个文件里面。如上面分析思路,最终拦截就在这个方法里面去匹配WIFI列表,同步对比自己的白名单。
解决需求思路就是在这个类中去维护一个白名单列表。 对应的源码如下:
// modeify add
private List<String> ssid_whiteList=new ArrayList<>();
public void setWiFiWhiteList(List<String> whiteList){
this.ssid_whiteList=whiteList;
}
public List<String> getWiFiWhiteList(){
return this.ssid_whiteList;
}
// modeify end
/**
* Return the results of the most recent access point scan, in the form of
* a list of {@link ScanResult} objects.
* @return the list of results
*/
@Override
public List<ScanResult> getScanResults(String callingPackage, String callingFeatureId) {
enforceAccessPermission();
int uid = Binder.getCallingUid();
long ident = Binder.clearCallingIdentity();
if (mVerboseLoggingEnabled) {
mLog.info("getScanResults uid=%").c(uid).flush();
}
try {
mWifiPermissionsUtil.enforceCanAccessScanResults(callingPackage, callingFeatureId,
uid, null);
List<ScanResult> scanResults = mWifiThreadRunner.call(
mScanRequestProxy::getScanResults, Collections.emptyList());
// modeify add
Log.d(TAG,"getScanResults result size:"+scanResults.size());
for (String whiteSSID : this.ssid_whiteList) {
//System.out.println(item);
Log.e(TAG, "getScanResults whiteSSID:"+whiteSSID);
}
if(this.ssid_whiteList!=null && this.ssid_whiteList.size()!=0){ // zhi you white list caihui pipei
List<ScanResult> result1 =new ArrayList<>();
for (ScanResult resultbean : scanResults){
Log.e(TAG, "getScanResults ssid_whiteList:"+ssid_whiteList+" resultbean.SSID:"+resultbean.SSID );
// zhiyou zai baimingdan limian de ssid caihui xianshi chulai
if(!this.ssid_whiteList.contains(resultbean.SSID)){ // pipei gongzuo
Log.e(TAG, "getScanResults not in whiteList no add to show resultbean.SSID:"+resultbean.SSID);
continue;
}
result1.add(resultbean);
}
Log.e(TAG," there is whitelist so to return filter results result1:"+result1);
return result1;
}
// modeify end
Log.e(TAG, "getScanResults final to return scanResults:"+scanResults);
return scanResults;
} catch (SecurityException e) {
Log.e(TAG, "Permission violation - getScanResults not allowed for uid="
+ uid + ", packageName=" + callingPackage + ", reason=" + e);
return new ArrayList<>();
} finally {
Binder.restoreCallingIdentity(ident);
}
}
小结
这里设计到framework层wifi 相关的四个类的修改,这里暂不延伸源码和业务分析,本身大家日常开发对WIFI 应该不陌生的。结合实现需求的思路,应该很容易理解并找到源码对应的位置。
四、测试验证
既然思路和Framework 层的逻辑已经写好了的,那么总要验证一下吧,这样app同事好对接,自己也要验证下功能是否OK吧。
获取framework.jar 包
我自己在实现了 Framework层功能后,自己写个app 验证,功能是否实现了需求。 自己想法就是 自己改了那么文件,固件也编译到了主板机器上了,系统都跑起来了。但是应用如何调用呢?
按照自己以往的经验,自己在项目开发时候 直接拿 系统的framwork.jar ,然后直接调用、引用系统的类,进行系统类进行直接调用。 如下 常用的 framework.jar 引用和framework 层类的调用。
所以自己在应用层调用刚才在framework层的wifi 相关接口方法,第一反应就是去找framework.jar 文件。 那就在系统里面找framework.jar 文件。
这里 用find 命令找路径:
fise4@ubuntu-PowerEdge-R730:~/Android/rk/Rockchip_RK356X_Android11_CloudDesk_SDK_RELEASE$ find . -name framework.jar
./out/soong/rk3566_r/dex_bootjars_input/framework.jar
./out/soong/.intermediates/frameworks/base/framework/android_common/turbine-combined/framework.jar
./out/target/product/rk3566_r/obj/PACKAGING/target_files_intermediates/rk3566_r-target_files-eng.fise4/SYSTEM/framework/framework.jar
./out/target/product/rk3566_r/system/framework/framework.jar
fise4@ubuntu-PowerEdge-R730:~/Android/rk/Rockchip_RK356X_Android11_CloudDesk_SDK_RELEASE$
这里发现有好几个,那么就具体确定具体是哪一个吧:实际发现并不是每个framework.jar 里面都一样的,我们就是要找到具体是哪一个framework.jar 是 系统编译出来的,包含所有的源码文件。
那就解析jar 包,查看 是否含有WifiManager 相关类来查找:
jar tf framework.jar | grep WifiManager
最终确认路径如下: 居然和网上说的都不一样,所以需要自己实际操作才有相关的经验和感受的,找到正确的 framework.jar
\out\soong\.intermediates\frameworks\base\framework\android_common\turbine-combined\framework.jar
这里拿到jar 包去验证自己的猜想,解析jar 看看,里面是不是有自己修改过的文件:
哈哈,这里我们基本上确认,我们引用的jar 是没问题的。
引用framework.jar 实现功能
当你把自己需要的jar 包引用到AS 项目里面,满心欢喜准备直接调用的时候,沉重的一击是 发现根本没有自己写的方法。 自己尝试好多遍验证是不是自己framework.jar 引用错误了,实际发现没有的,在项目里面应用jar 就是无法加载自己修改类,添加的方法! 【这里暂不扩展,机制问题】
反射调用WIFI 扩展的相关方法
当上面两个历程走完后,一脸懵逼。 如果不行就直接发射调用呗,系统framework层已经也一定有相关的实现,其实就是自己应用层如何调用framework层问题。那就直接发射呗。
问题:反射什么? 反射调用设置和获取白名单方法功能。
反射实现设置白名单 setWiFiWhiteList
fun setWifiWhiteList( whiteList: List<String>): Boolean {
return try {
val wifiManager = ContextProvider.get().context.getSystemService(Context.WIFI_SERVICE) as WifiManager
// 通过反射获取 setWiFiWhiteList 方法
val method: Method = wifiManager.javaClass.getMethod(
"setWiFiWhiteList",
List::class.java
)
// 调用方法设置白名单
method.invoke(wifiManager, whiteList)
true // 操作成功
} catch (e: Exception) {
e.printStackTrace()
false // 操作失败
}
}
实际调用反射
val wifiManager = getSystemService(WIFI_SERVICE) as WifiManager
if (wifiManager.isWifiEnabled()) {
// WiFi 已启用
Log.d(TAG," WiFi 已启用")
// 设置 WiFi 白名单
val success = setWifiWhiteList(
listOf("HomeWiFi", "OfficeWiFi", "TrustedNetwork","fiseGMS","FiseTest_5G","FiseTest")
)
if (success) {
Log.d("WifiConfig", "WiFi 白名单设置成功")
} else {
Log.e("WifiConfig", "WiFi 白名单设置失败")
}
} else {
Log.d(TAG," WiFi 未启用")
}
反射实现获取白名单 getWiFiWhiteList
fun getWifiWhiteList(): List<String>? {
return try {
val wifiManager =ContextProvider.get().context.getSystemService(Context.WIFI_SERVICE) as WifiManager
// 通过反射获取 getWiFiWhiteList 方法
val method: Method = wifiManager.javaClass.getMethod("getWiFiWhiteList")
// 调用方法并获取结果
val result = method.invoke(wifiManager)
// 转换为 List<String>
result as? List<String>
} catch (e: Exception) {
e.printStackTrace()
null
}
}
实际调用反射
viewBinding.getWifiwhitelist.setOnClickListener {
Log.d(TAG, "get_Wifiwhitelist WIFI 白名单获取")
val wifiManager = getSystemService(WIFI_SERVICE) as WifiManager
if (wifiManager.isWifiEnabled()) {
// WiFi 已启用
Log.d(TAG," WiFi 已启用")
val whiteWifiList=getWifiWhiteList()
Log.d(TAG,"white_WifiList:${whiteWifiList}")
// wifiManager
} else {
Log.d(TAG," WiFi 未启用")
}
}
效果
这里给出实际 效果如吐下
默认的WIFI列表
设置白名单后的wifi 列表
一些日志打印
默认获取 wifi 白名单
设置一次白名单
获取白名单-设置白名单后
总结
- 假使你已经对WIIF相关基础有一定的了解,这边文章和推荐资料能帮你实现需求,很容易理解,和定制相关WIFI功能
- 实际经验来看,framework.jar 就是无法调用到自己修改的文件方法,不妨换个思路,反射。
- 这里只是在服务类里面维护了一个内存结合List 类型的白名单。实际开发中 维护本地存储就可以了。 这样方便保存 满足实际开发需求。