预置wifi

要求

系统版本

Android 11

要求

预置Wi-Fi,开机自连

思路

根据Wi-Fi自己连接的流程来看,Wi-Fi自己的设置文件是保存在 /data/misc/apexdata/com.android.wifi/WifiConfigStore.xml(仅针对Android 11 版本)。
那么我们在预置Wi-Fi时,有一种思路就是去用保存有目标Wi-Fi 信息的 WifiConfigStore.xml 文件去替换初始文件,即可实现预置Wi-Fi。为了保证用户以后添加的Wi-Fi信息不被覆盖,我们需要保证设备在第一次开机以后只覆盖一次。

关于替换文件这个操作,有两种实现的思路。一是在系统启动时,把编译时拷贝到指定目录的该文件再次拷贝到 Wi-Fi 保存文件路径下,然后限定触发条件。二是在编译时把文件拷贝到指定目录,然后在读取Wi-Fi信息时增加从指定目录读取预置文件的方法。笔者针对上述两种思路都进行了尝试,都能达到预设的期望。接下来笔者将简略的讲述一下实现的过程。

实现

首先在 userdebug 版本下先连接好指定 ssid,将 WifiConfigStore.xml copy 出来加入源码目录。然后在编译过程中,将该文件拷贝至指定目录
推荐在vendor目录下设备厂商目录添加。

PRODUCT_COPY_FILES += ***/***/WifiConfigStore.xml:system/etc/WifiConfigStore.xml

思路1: 在init.rc中去覆盖文件。
system\core\rootdir\init.rc

on property:xxx.xxx=1
    copy /system/etc/WifiConfigStore.xml /data/misc/apexdata/com.android.wifi/WifiConfigStore.xml

然后去某个地方触发一次该操作即可。即设置 xxx.xxx 属性的值等于1。一般来说可以添加至 packages/apps/Provision 触发。至于为什么选择在这里触发,可以阅读参考文件3 。

思路2: 增加读取方法。
这里的思路就是模仿原生Wi-Fi读取自身设置信息的方法,去读取指定目录下的设置信息。要注意,我们在新增方法的时候,一定要注意参考原本读取 Wi-Fi 信息的方法。
添加的位置是 /frameworks/opt/net/wifi/service/java/com/android/server/wifi/WifiConfigStore.java

private static final File SYSTEM_CONFIG_FILE = new File("/system/etc/WifiConfigStore.xml");
    private final Context mContext;
    ......
    public WifiConfigStore(Context context, Handler handler, Clock clock, WifiMetrics wifiMetrics,
            List<StoreFile> sharedStores) {

        mContext = context;
        ......
    }
    ......
    private void readFromLocalStoreFiles() throws XmlPullParserException,IOException {
        if (Settings.Global.getInt(mContext.getContentResolver(), "xxx.xxx.xxx", 0) == 1 || !SYSTEM_CONFIG_FILE.exists()) {
            Log.d(TAG, "readFromLocalStoreFiles ignore");
            return ;
        }
        for (StoreFile sharedStoreFile : mSharedStores) {
            if(sharedStoreFile.getFileId() == STORE_FILE_SHARED_GENERAL) {
                InputStream migrationIs = new AtomicFile(SYSTEM_CONFIG_FILE).openRead();
                if (migrationIs != null) {
                    byte[] sharedDataBytes = readAtomicFileFully(migrationIs);
                    if (sharedDataBytes == null) {
                        sharedDataBytes = sharedStoreFile.readRawData();
                     } else {
                        Log.i(TAG, "Read data out of shared migration store file: " + sharedStoreFile.getName());
                        sharedStoreFile.storeRawDataToWrite(sharedDataBytes);
                        sharedStoreFile.writeBufferedRawData();
                        WifiMigration.removeSharedConfigStoreFile(getMigrationStoreFileId(sharedStoreFile.getFileId()));
                    }
                    deserializeData(sharedDataBytes, sharedStoreFile);
                }
            }
        }
        Settings.Global.putInt(mContext.getContentResolver(), "xxx.xxx.xxx", 1);
    }
    .......

    readFromSharedStoreFiles();
    readFromLocalStoreFiles();

读取方法的大致内容就是这样。

以上两种方法笔者都做过测试,均能实现。如果有什么错误的地方或者新的思路,欢迎大家发表意见。

参考内容

  1. Android 8 预置 Wi-Fi
  2. RK3568 Android 11 已保存的WiFi,不自动连接
  3. Android Provision