Android 之 wifi 简介 & WifiService

Posted by Vector Blog on December 21, 2015

1.架构

framework 提供的接口为 android.net.wifi ( frameworks/base/wifi/java/android/net/wifi ),app直接通过其中的接口(主要有 WifiManager )来控制 wifi 。 其通过binder来调用wifiservice的服务,实际上 wifiservice 通过 socket 连接来向 wpa_supplicant 发送cmd,来实现对wifi的操作.

2.WifiService

WifiService 是 Android Java Framework 中负责 Wi-Fi 功能的核心服务。在开机时由 SystemServer 启动。

SystemServer.java

private static final String WIFI_SERVICE_CLASS = "com.android.server.wifi.WifiService";

mSystemServiceManager.startService(WIFI_SERVICE_CLASS);

WifiService.java

public final class WifiService extends SystemService {

    public WifiService(Context context) {
        super(context);
        mImpl = new WifiServiceImpl(context);
    }

    @Override
    public void onStart() {
        // bind SystemServer
        publishBinderService(Context.WIFI_SERVICE, mImpl);
    }

WifiService 的主要工作都是在 WifiServiceImpl 中完成:

    public WifiServiceImpl(Context context) {
        mContext = context;

        // 从系统属性“wifi.interface”中取出无线网络设备接口名。 默认值为“wlan0”
        mInterfaceName =  SystemProperties.get("wifi.interface", "wlan0");

        mTrafficPoller = new WifiTrafficPoller(mContext, mInterfaceName);

        // 创建一个WifiStateMachine对象, 它是WifiService相关模块中的核心		
        mWifiStateMachine = new WifiStateMachine(mContext, mInterfaceName, mTrafficPoller);
        mWifiStateMachine.enableRssiPolling(true);

        // 和BatteryStatsService交互
        mBatteryStats = BatteryStatsService.getService();
        mPowerManager = context.getSystemService(PowerManager.class);
        mAppOps = (AppOpsManager)context.getSystemService(Context.APP_OPS_SERVICE);
        mUserManager = UserManager.get(mContext);

        // 用于获取和修改 Settings 的 sqlite DB
        mSettingsStore = new WifiSettingsStore(mContext);

        // 广播事件注册等处理
        HandlerThread wifiThread = new HandlerThread("WifiService");
        wifiThread.start();
        // mClientHandler 用于 AsyncChannel, 其交互对象来自WifiManager
        mClientHandler = new ClientHandler(wifiThread.getLooper());

        // mWifiStateMachineHandler也用于AsyncChannel,其交互对象来自WifiStateMachine
        mWifiStateMachineHandler = new WifiStateMachineHandler(wifiThread.getLooper());
        mWifiController = new WifiController(mContext, this, wifiThread.getLooper());
    }

其中WifiStateMachine是WifiService中的核心 ,先来看一下它的构造函数 :

    public WifiStateMachine(Context context, String wlanInterface,
                            WifiTrafficPoller trafficPoller) {
        super("WifiStateMachine");
        mContext = context;

        // 获取 countery code
        mSetCountryCode = Settings.Global.getString(mContext.getContentResolver(), Settings.Global.WIFI_COUNTRY_CODE);
        mInterfaceName = wlanInterface;

        // 创建一个NetworkInfo, 它实际上代表一个网络设备的状态信息( status of a network interface)
        mNetworkInfo = new NetworkInfo(ConnectivityManager.TYPE_WIFI, 0, NETWORKTYPE, "");

        // 创建和NewtorkManagmentService交互的Binder客户端
        IBinder b = ServiceManager.getService(Context.NETWORKMANAGEMENT_SERVICE);
        mNwService = INetworkManagementService.Stub.asInterface(b);

        // WifiNative: 用于和wpa_supplicant交互
        mWifiNative = new WifiNative(mInterfaceName);

        // WifiConfigStore: 它对应一个配置文件,该文件用于存储每个无线网络的配置项, 例如代理地址、 静态IP地址等。
        mWifiConfigStore = new WifiConfigStore(context,this,  mWifiNative);

        mWifiAutoJoinController = new WifiAutoJoinController(context, this,mWifiConfigStore, mWifiConnectionStatistics, mWifiNative);

        // WifiMonitor: 内部将创建一个线程, 并借助WifiNative去接收并处理来自WPAS的信息。
        mWifiMonitor = new WifiMonitor(this, mWifiNative);

        // WifiInfo用于存储手机当前连接上的无线网络的一些信息, 包括IP地址、 ssid等内容
        mWifiInfo = new WifiInfo();

        // SupplicantStateTracker用于跟踪WPAS的状态, 它也是一个StateMachine
        mSupplicantStateTracker = new SupplicantStateTracker(context, this, mWifiConfigStore, getHandler());

        mNetworkInfo.setIsAvailable(false);

        // 状态机初始化
        ......       

以下为状态机的各个状态 ,其初始状态为 InitialState :

2.1 WifiNative

下面重点看一下 WifiNative , 它用于和 WPAS 通信,其内部定义了较多的native方法( 对应的JNI模块是android_net_wifi_Wifi) 。下面介绍其中最重要的两个方法 :

startSupplicant

用于启动WPAS, startSupplicant是一个native函数, 其 JNI 函数为 android_net_wifi_startSupplicant, 代码如下所示。

android_net_wifi_Wifi.c::android_net_wifi_startSupplicant

static jboolean android_net_wifi_startSupplicant(JNIEnv* env, jobject, jboolean p2pSupported)
{
    return (::wifi_start_supplicant(p2pSupported) == 0);
}

wifi.c::wifi_start_supplicant

int wifi_start_supplicant(int p2p_supported)
{
    char supp_status[PROPERTY_VALUE_MAX] = {'\0'};
    int count = 200; /* wait at most 20 seconds for completion */
    const prop_info *pi;
    unsigned serial = 0, i;

    if (p2p_supported) {
        strcpy(supplicant_name, P2P_SUPPLICANT_NAME);	//P2P_SUPPLICANT_NAME[] = "p2p_supplicant";
        strcpy(supplicant_prop_name, P2P_PROP_NAME);

        /* Ensure p2p config file is created */
        if (ensure_config_file_exists(P2P_CONFIG_FILE, SUPP_CONFIG_TEMPLATE) < 0) {
            ALOGE("Failed to create a p2p config file");
            return -1;
        }
    } else {
        strcpy(supplicant_name, SUPPLICANT_NAME);	//SUPPLICANT_NAME[] = "wpa_supplicant";
        strcpy(supplicant_prop_name, SUPP_PROP_NAME);	//SUPP_PROP_NAME[] = "init.svc.wpa_supplicant";
    }

    // 如果WPAS已经启动, 则直接返回
    if (property_get(supplicant_prop_name, supp_status, NULL)
            && strcmp(supp_status, "running") == 0) {
        return 0;
    }

    /* Before starting the daemon, make sure its config file exists */
    if (ensure_config_file_exists(SUPP_CONFIG_FILE, SUPP_CONFIG_TEMPLATE) < 0) {
        ALOGE("Wi-Fi will not be enabled");
        return -1;
    }

    // entropy文件, 用于增加随机数生成的随机性
    if (ensure_entropy_file_exists() < 0) {
        ALOGE("Wi-Fi entropy file was not created");
    }

    // 关闭之前创建的wpa_ctrl对象
    wpa_ctrl_cleanup();

    /* Reset sockets used for exiting from hung state */
    exit_sockets[0] = exit_sockets[1] = -1;


    /*
    通过设置“ctrl.start”属性来启动wpa_supplicant服务。 该属性将触发
    init fork一个子进程用于运行wpa_supplicant。 同时, init还会添加一个新的属性
    “init.svc.wpa_supplicant”用于跟踪wpa_supplicant的状态。
    */
    pi = __system_property_find(supplicant_prop_name);
    if (pi != NULL) {
        serial = __system_property_serial(pi);
    }
    property_get("wifi.interface", primary_iface, WIFI_TEST_INTERFACE);

    property_set("ctl.start", supplicant_name);
    sched_yield();

    while (count-- > 0) {
        if (pi == NULL) {
            pi = __system_property_find(supplicant_prop_name);
        }
        if (pi != NULL) {
            /*
             * property serial updated means that init process is scheduled
             * after we sched_yield, further property status checking is based on this */
            if (__system_property_serial(pi) != serial) {
                __system_property_read(pi, NULL, supp_status);
                if (strcmp(supp_status, "running") == 0) {
                    return 0;
                } else if (strcmp(supp_status, "stopped") == 0) {
                    return -1;
                }
            }
        }
        usleep(100000);
    }
    return -1;
}

connectToSupplicant

另外一个比较重要的函数就是 connectToSupplicant ,它将通过WPAS控制API和WPAS建立交互关系。

int wifi_connect_to_supplicant()
{
    static char path[PATH_MAX];

    // char IFACE_DIR[] = "/data/system/wpa_supplicant" ,目前看到的手机中都没有这个路径
    if (access(IFACE_DIR, F_OK) == 0) {
        snprintf(path, sizeof(path), "%s/%s", IFACE_DIR, primary_iface);
    } else {
        snprintf(path, sizeof(path), "@android:wpa_%s", primary_iface);
    }
    return wifi_connect_on_socket_path(path);
}

int wifi_connect_on_socket_path(const char *path)
{
    char supp_status[PROPERTY_VALUE_MAX] = {'\0'};

    // 判断wpa_supplicant进程是否已经启动
    if (!property_get(supplicant_prop_name, supp_status, NULL)
            || strcmp(supp_status, "running") != 0) {
        ALOGE("Supplicant not running, cannot connect");
        return -1;
    }

    // 创建第一个wpa_ctrl对象, 用于发送命令
    ctrl_conn = wpa_ctrl_open(path);

    // 创建第二个wpa_ctrl对象, 用于接收unsolicited event
    monitor_conn = wpa_ctrl_open(path);

    // 必须调用wpa_ctrl_attach函数以启用 unsolicited event接收功能
    if (wpa_ctrl_attach(monitor_conn) != 0) {
        ......
    }
    
    // 创建一个socketpair, 它用于触发WifiNative关闭和WPAS的连接
    if (socketpair(AF_UNIX, SOCK_STREAM, 0, exit_sockets) == -1) {
        ......
    }

    return 0;
}

之后在 wifi.c中, wifi_send_command 会使用ctrl_conn中的wpa_ctrl对象向WPAS发送命令并接收回复, 而 wifi_ctrl_recv 函数将使用monitor_conn中的wpa_ctrl对象接收来自WPAS的消息。

2.2 WifiMonitor

WifiMonitor最重要的内容是其内部的WifiMonitor线程, 该线程专门用于接收来自WPAS的消息。

    private static class MonitorThread extends Thread {
        ......

        public MonitorThread(WifiNative wifiNative, WifiMonitorSingleton wifiMonitorSingleton) {
            super("WifiMonitor");
            mWifiNative = wifiNative;
            mWifiMonitorSingleton = wifiMonitorSingleton;
        }

        public void run() {
            if (DBG) {
                Log.d(TAG, "MonitorThread start with mConnected=" +
                     mWifiMonitorSingleton.mConnected);
            }
            //noinspection InfiniteLoopStatement
            for (;;) {
                if (!mWifiMonitorSingleton.mConnected) {
                    if (DBG) Log.d(TAG, "MonitorThread exit because mConnected is false");
                    break;
                }

                // waitForEvent内部会调用wifi.c中的wifi_wait_on_socket -> wifi_ctrl_recv 函数
                String eventStr = mWifiNative.waitForEvent();

                ......

                // 事件处理
                if (mWifiMonitorSingleton.dispatchEvent(eventStr)) {
                    if (DBG) Log.d(TAG, "Disconnecting from the supplicant, no more events");
                    break;
                }
            }
        }
    }

2.3 WifiController

WifiController 也是一个状态机 , 其状态如下图:

        if (isScanningAlwaysAvailable) {
            setInitialState(mStaDisabledWithScanState);
        } else {
            setInitialState(mApStaDisabledState);
        }

待补充…