WifiManage hotspot热点设置分析(高通平台 android 4.3)

WifiManage hotspot设置分析

 

高通MSM8226 平台提供了对hotspot的设置,通过setting->more->Tethering&portable hotspot的Portable Wi-Fihotspot 可以设置手机WIFI提供热点接入的功能,那么具体流程与原理是什么呢?

 

在\packages\apps\Settings\src\com\android\settings\wifi\WifiApEnabler.java文件中可以看到相关的具体的设置函数setSoftapEnabled,代码大致如下

 

/*如果hotspot 设置使能,并且现在wificlient出于打开状态,首先关闭WIFI*/

int wifiState =mWifiManager.getWifiState();

       if (enable && ((wifiState == WifiManager.WIFI_STATE_ENABLING) ||

                    (wifiState == WifiManager.WIFI_STATE_ENABLED))){

           mWifiManager.setWifiEnabled(false);        }

 

/*调用这个WIFIManager类的接口设置AP使能,一会我们主要分析这个函数的实现*/

       if (mWifiManager.setWifiApEnabled(null, enable)) {

           /* Disable here, enabled on receiving success broadcast */

           mCheckBox.setEnabled(false);

       } else {

           mCheckBox.setSummary(R.string.wifi_error);

       }

/*当禁止AP时,查看之前WIFIclient状态,并恢复*/

       if (!enable) {

           if (wifiSavedState == 1) {

               mWifiManager.setWifiEnabled(true);

                Settings.Global.putInt(cr,Settings.Global.WIFI_SAVED_STATE, 0);

           }

       }

 

看来是WiFiManager这个类提供了setWifiApEnabled的接口,下面我从framework开始分析setWifiApEanble这个函数,

这个函数位于\frameworks\base\wifi\java\android\net\wifi\WifiManager.java, 函数实现如下

 

/*通过mService类又调用了setWifiApEnabled*/

   public boolean setWifiApEnabled(WifiConfiguration wifiConfig, booleanenabled) {

       try {

           mService.setWifiApEnabled(wifiConfig, enabled);

           return true;

       } catch (RemoteException e) {

           return false;

       }

}

 

好的,看看mService是一个什么东西,其定义如下

IWifiManagermService;

 

??? IWifiManager是什么,通过搜索他的定义,无果,后来发现,在WifiManager.java同名目录下有一个IWifiManager.aidl文件,.aidl 全称android 接口描述语言,类似j2ee中的RMI, RMI是通过序列化传递对象实现跨进程数据共享,AIDL通过定义接口实现跨进程的数据共享 IWifiManager.aidl部分文件如下

packageandroid.net.wifi;

importandroid.net.wifi.PPPOEConfig;

importandroid.net.wifi.PPPOEInfo;

importandroid.net.wifi.WifiInfo;

importandroid.net.wifi.WifiConfiguration;

importandroid.net.wifi.ScanResult;

importandroid.net.DhcpInfo;

importandroid.os.Messenger;

importandroid.os.WorkSource;

 

/*这些个接口提供访问控制wifi的链接*/

interfaceIWifiManager

{

    List<WifiConfiguration>getConfiguredNetworks();

 

    int addOrUpdateNetwork(in WifiConfigurationconfig);

 

    boolean removeNetwork(int netId);

 

    boolean enableNetwork(int netId, booleandisableOthers);

 

    boolean disableNetwork(int netId);

 

    boolean pingSupplicant();

……此处省略N个字

}

 

通过搜索可以知道,其调用WifiService.java中的函数,代码如下

    public voidsetWifiApEnabled(WifiConfiguration wifiConfig, boolean enabled) {

        enforceChangePermission();

        mWifiController.obtainMessage(CMD_SET_AP,enabled ? 1 : 0, 0, wifiConfig).sendToTarget();

    }

可以看出,该函数通过CMD_SET_AP 命令进行设置,mWifiController.obtainMessage将message发送到消息处理函数,进一步的处理在WifiController中实现,WifiController继承了StateMachine,在StateMachine中实现了obtainMessage方法。该方法通过Message实现

Message.obtain(mSmHandler,what);

最终的调用结果如下

public staticMessage obtain(Handler h, int what,

            int arg1, int arg2, Object obj) {

        Message m = obtain();

        m.target = h;

        m.what = what;

        m.arg1 = arg1;

        m.arg2 = arg2;

        m.obj = obj;

 

        return m;

    }

实际上对Message进行了初始化,信息通过Message对象的sendToTarget发送出去。

这个消息通过wificontroll.java中的ApStaDisabledState类中消息处理函数进行处理,代码如下

case CMD_SET_AP:

                    if (msg.arg1 == 1) {

                       mWifiStateMachine.setHostApRunning((WifiConfiguration) msg.obj,

                                true);

                       transitionTo(mApEnabledState);

                    }

                    break;

 

主要通过调用WifiStateMachine中的方法实现对hostAp的设置,该函数定义如下

public voidsetHostApRunning(WifiConfiguration wifiConfig, boolean enable) {

        if (enable) {

            sendMessage(CMD_START_AP,wifiConfig);

        } else {

            sendMessage(CMD_STOP_AP);

        }

    }

 

又是message,继续搜索CMD_START_AP,这个类的子类InitialState中的processMessage函数有对这个命令字的处理

                case CMD_START_AP:

                    if(mWifiNative.loadDriver()) {

                       setWifiApState(WIFI_AP_STATE_ENABLING);

                       transitionTo(mSoftApStartingState);

                    } else {

                        loge("Failed toload driver for softap");

                    }

 

mWifiNative.loadDriver通过调用JNI接口加载驱动,其实现在android_net_wifi_Wifi.cpp文件的android_net_wifi_loadDriver函数,实现如下

 

staticJNINativeMethod gWifiMethods[] = {

    /* name, signature, funcPtr */

 

    { "loadDriver","()Z",  (void*)android_net_wifi_loadDriver },

 

此处省略N个字

}

 

setWifiApState(WIFI_AP_STATE_ENABLING);函数实现对WIFI状态向其他模块的通知,之后通过transitionTo(mSoftApStartingState); 进入到AP 启动中的状态,SoftApStartingState状态的enter函数中,这个函数很关键,走读到现在才发现最为关键的部分。

public voidenter() {

            final Message message =getCurrentMessage();

                            /*如果要开启AP*/

            if (message.what == CMD_START_AP) {

                final WifiConfiguration config= (WifiConfiguration) message.obj;

                                     /*obj这个参数实际上刚开始mWifiManager.setWifiApEnabled(null,enable)的null,所以这个时候为空,走这个分支*/

                if (config == null) {

                   if (startSafeChannel!=0) {

                       Log.e(TAG, "CallingsetChannelRange ---CMD_START_AP SoftApStartingState()");

                      setChannelRange(startSafeChannel, endSafeChannel , 0);

                   }

                   mWifiApConfigChannel.sendMessage(CMD_REQUEST_AP_CONFIG);

                } else {/*没走到这里*/

                   mWifiApConfigChannel.sendMessage(CMD_SET_AP_CONFIG, config);

                   startSoftApWithConfig(config);

                }

            } else {

                throw newRuntimeException("Illegal transition to SoftApStartingState: " +message);

            }

        }

setChannelRange应该是wifichannel情况的一些设置,我们暂时不管他。

从上面代码中看出,mWifiApConfigChannel.sendMessage(CMD_REQUEST_AP_CONFIG)函数进一步设置,好吧,继续看CMD_REQUEST_AP_CONFIG,

这个AP设置请求的命令实际被WifiApConfigStore接收,如下

caseWifiStateMachine.CMD_REQUEST_AP_CONFIG:

                   mReplyChannel.replyToMessage(message,

                           WifiStateMachine.CMD_RESPONSE_AP_CONFIG, mWifiApConfig);

可以看出这实际上又返回了一个CMD_REQUEST_AP_CONFIG消息给SoftApStartingState状态,在将焦点集中到这个类的消息处理函数

caseWifiStateMachine.CMD_RESPONSE_AP_CONFIG:

                    WifiConfiguration config =(WifiConfiguration) message.obj;

                    if (config != null) { /*获取到了配置,正常不应该为空,走到这里*/

                       startSoftApWithConfig(config);

                    } else {

                        loge("Softapconfig is null!");

                       sendMessage(CMD_START_AP_FAILURE);

                    }

                    break;

 

继续关注startSoftApWithConfig(config);函数,其实现如下

    private void startSoftApWithConfig(finalWifiConfiguration config) {

        // start hostapd on a seperate thread

        new Thread(new Runnable() {

            public void run() {

                try {

                    mNwService.startAccessPoint(config,mInterfaceName);

                } catch (Exception e) {

                    不用去关注,省略

                }

                if (DBG) log("Soft APstart successful");

               sendMessage(CMD_START_AP_SUCCESS);

            }

        }).start();

    }

 

这个函数实现,启动了一个线程,可能配置需要时间,为了不阻塞用户而设计。startAccessPoint有用到了AIDL方法,调用其他进程函数,其调用了NetworkManagementService.java 的函数

    public void startAccessPoint(

            WifiConfiguration wifiConfig,String wlanIface) {

       mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);

        try {

            wifiFirmwareReload(wlanIface,"AP"); 、/*加载AP*/

            if (wifiConfig == null) {

               mConnector.execute("softap", "set", wlanIface);

            } else {

                                     /*执行了下面的函数*/

               mConnector.execute("softap", "set", wlanIface,wifiConfig.SSID,

                       getSecurityType(wifiConfig), new SensitiveArg(wifiConfig.preSharedKey));

            }

           mConnector.execute("softap", "startap");

        } catch (NativeDaemonConnectorExceptione) {

            throwe.rethrowAsParcelableException();

        }

    }

 

可以看出其通过softap进行了AP的相关设置,softap的设置代码在NativeDaemonConnector类中,下面在framework是没有了,我们现在可以看出hotspot的设置实际是放在HAL层实现的,这也符合android设计的易图,因为HAL层能够隐藏厂商的设计,保护厂商的利益。简单看一下execute成员的实现

publicNativeDaemonEvent[] execute(int timeout, String cmd, Object... args)

            throwsNativeDaemonConnectorException {

        final long startTime =SystemClock.elapsedRealtime();

 

        finalArrayList<NativeDaemonEvent> events = Lists.newArrayList();

 

        final StringBuilder rawBuilder = newStringBuilder();

        final StringBuilder logBuilder = newStringBuilder();

        final int sequenceNumber =mSequenceNumber.incrementAndGet();

                            /*生成了命令*/

        makeCommand(rawBuilder, logBuilder,sequenceNumber, cmd, args);

 

        final String rawCmd =rawBuilder.toString();

        final String logCmd =logBuilder.toString();

 

       log("SND -> {" + logCmd + "}");

 

        synchronized (mDaemonLock) {

            if (mOutputStream == null) {

                throw newNativeDaemonConnectorException("missing output stream");

            } else {

                try {

                    mOutputStream.write(rawCmd.getBytes(Charsets.UTF_8));

                } catch (IOException e) {

                    throw newNativeDaemonConnectorException("problem sending command", e);

                }

            }

        }

 

        NativeDaemonEvent event = null;

        do {

            event =mResponseQueue.remove(sequenceNumber, timeout, logCmd);

            if (event == null) {

                loge("timed-out waitingfor response to " + logCmd);

                throw newNativeDaemonFailureException(logCmd, event);

            }

            log("RMV <- {" + event+ "}");

            events.add(event);

        } while (event.isClassContinue());

 

        final long endTime =SystemClock.elapsedRealtime();

        if (endTime - startTime >WARN_EXECUTE_DELAY_MS) {

            loge("NDC Command {" +logCmd + "} took too long (" + (endTime - startTime) +"ms)");

        }

 

        if (event.isClassClientError()) {

            throw newNativeDaemonArgumentException(logCmd, event);

        }

        if (event.isClassServerError()) {

            throw newNativeDaemonFailureException(logCmd, event);

        }

 

        return events.toArray(newNativeDaemonEvent[events.size()]);

    }

 

上面的函数大概意思是生成一个命令,并通过localSocket传到对端。

在system目录中实现了对softAP的具体设置,实现为SoftapController.cpp文件的SoftapController::setSoftap函数

从整个分析过程来看,androidframework运用了大量的AIDL通信,message消息机制,以及状态机处理方法,分析这些方法对理清整个设置流程至关重要。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章