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消息機制,以及狀態機處理方法,分析這些方法對理清整個設置流程至關重要。

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