Android WIFI 模块解析(2)

Android WIFI 模块解析(2)

书接上文,这一章接着分析wifi模块的Hal层的调用逻辑.先贴一下上章的地址.

Android WIFI 模块解析(1)

public String setupInterfaceForClientMode(boolean lowPrioritySta,
        @NonNull InterfaceCallback interfaceCallback) {
    synchronized (mLock) {
        //启动hal层.  
        if (!startHal()) {
            Log.e(TAG, "Failed to start Hal");
            mWifiMetrics.incrementNumSetupClientInterfaceFailureDueToHal();
            return null;
        }
        //启动wpa_supplicant 
        if (!startSupplicant()) {
            Log.e(TAG, "Failed to start supplicant");
            mWifiMetrics.incrementNumSetupClientInterfaceFailureDueToSupplicant();
            return null;
        }
        Iface iface = mIfaceMgr.allocateIface(Iface.IFACE_TYPE_STA);
        if (iface == null) {
            Log.e(TAG, "Failed to allocate new STA iface");
            return null;
        }
        iface.externalListener = interfaceCallback;
        iface.name = createStaIface(iface, lowPrioritySta);
        if (TextUtils.isEmpty(iface.name)) {
            Log.e(TAG, "Failed to create STA iface in vendor HAL");
            mIfaceMgr.removeIface(iface.id);
            mWifiMetrics.incrementNumSetupClientInterfaceFailureDueToHal();
            return null;
        }
        if (mWificondControl.setupInterfaceForClientMode(iface.name) == null) {
            Log.e(TAG, "Failed to setup iface in wificond on " + iface);
            teardownInterface(iface.name);
            mWifiMetrics.incrementNumSetupClientInterfaceFailureDueToWificond();
            return null;
        }
        if (!mSupplicantStaIfaceHal.setupIface(iface.name)) {
            Log.e(TAG, "Failed to setup iface in supplicant on " + iface);
            teardownInterface(iface.name);
            mWifiMetrics.incrementNumSetupClientInterfaceFailureDueToSupplicant();
            return null;
        }
        iface.networkObserver = new NetworkObserverInternal(iface.id);
        if (!registerNetworkObserver(iface.networkObserver)) {
            Log.e(TAG, "Failed to register network observer on " + iface);
            teardownInterface(iface.name);
            return null;
        }
        mWifiMonitor.startMonitoring(iface.name);
        // Just to avoid any race conditions with interface state change callbacks,
        // update the interface state before we exit.
        onInterfaceStateChanged(iface, isInterfaceUp(iface.name));
        initializeNwParamsForClientInterface(iface.name);
        Log.i(TAG, "Successfully setup " + iface);
        return iface.name;
    }
}
复制代码

这是上一章中贴出的最后一个函数,本章我们重点分析startHal()函数和startSupplicant()函数.

startHal

调用过程

我们先追逐一下其代码过程

startHal

/** Helper method invoked to start supplicant if there were no ifaces */
private boolean startHal() {
    synchronized (mLock) {
        if (!mIfaceMgr.hasAnyIface()) {
            //判断其是否当前的设备供应商Hal层是否可用
            if (mWifiVendorHal.isVendorHalSupported()) {
                //启动设备Hal
                if (!mWifiVendorHal.startVendorHal()) {
                    Log.e(TAG, "Failed to start vendor HAL");
                    return false;
                }
            } else {
                Log.i(TAG, "Vendor Hal not supported, ignoring start.");
            }
        }
        return true;
    }
}
复制代码

WifiVendorHal

这个类注释是厂商hal层对HIDL的支持,HIDL的发音hide-l.后文专门解释一下这个东西

/**
 * Vendor HAL via HIDL
 */
public class WifiVendorHal {
    
    ...
    /**
     * Bring up the HIDL Vendor HAL.
     * @return true on success, false otherwise.
     */
    public boolean startVendorHal() {
        synchronized (sLock) {
            // 在这里启动
            if (!mHalDeviceManager.start()) {
                mLog.err("Failed to start vendor HAL").flush();
                return false;
            }
            mLog.info("Vendor Hal started successfully").flush();
            return true;
        }
    }
    ...
}
复制代码

HalDeviceManager

/**
 * Handles device management through the HAL (HIDL) interface.
 */
public class HalDeviceManager {

    /**
     * Attempts to start Wi-Fi (using HIDL). Returns the success (true) or failure (false) or
     * the start operation. Will also dispatch any registered ManagerStatusCallback.onStart() on
     * success.
     *
     * Note: direct call to HIDL.
     */
    public boolean start() {
        return startWifi();
    }
    
    private boolean startWifi() {
        if (VDBG) Log.d(TAG, "startWifi");

        synchronized (mLock) {
            try {
                if (mWifi == null) {
                    Log.w(TAG, "startWifi called but mWifi is null!?");
                    return false;
                } else {
                    int triedCount = 0;
                    while (triedCount <= START_HAL_RETRY_TIMES) {
                        //在这里
                        WifiStatus status = mWifi.start();
                        if (status.code == WifiStatusCode.SUCCESS) {
                            initIWifiChipDebugListeners();
                            managerStatusListenerDispatch();
                            if (triedCount != 0) {
                                Log.d(TAG, "start IWifi succeeded after trying "
                                         + triedCount + " times");
                            }
                            return true;
                        } else if (status.code == WifiStatusCode.ERROR_NOT_AVAILABLE) {
                            // Should retry. Hal might still be stopping.
                            Log.e(TAG, "Cannot start IWifi: " + statusString(status)
                                    + ", Retrying...");
                            try {
                                Thread.sleep(START_HAL_RETRY_INTERVAL_MS);
                            } catch (InterruptedException ignore) {
                                // no-op
                            }
                            triedCount++;
                        } else {
                            // Should not retry on other failures.
                            Log.e(TAG, "Cannot start IWifi: " + statusString(status));
                            return false;
                        }
                    }
                    Log.e(TAG, "Cannot start IWifi after trying " + triedCount + " times");
                    return false;
                }
            } catch (RemoteException e) {
                Log.e(TAG, "startWifi exception: " + e);
                return false;
            }
        }
    }

}
复制代码

在这里我们看到mWifi这个对象去调用了start()函数通过hal层去启动与内核通信.那么mWifi这个对象在何时创建.

import android.hardware.wifi.V1_0.IWifi;

/**
 * Wrapper function to access the HIDL services. Created to be mockable in unit-tests.
 */
protected IWifi getWifiServiceMockable() {
    try {
        return IWifi.getService();
    } catch (RemoteException e) {
        Log.e(TAG, "Exception getting IWifi service: " + e);
        return null;
    }
}
复制代码

我们看下IWifi到底是何物

IWifi


package android.hardware.wifi@1.0;

import IWifiChip;
import IWifiEventCallback;

/**
 * This is the root of the HAL module and is the interface returned when
 * loading an implementation of the Wi-Fi HAL. There must be at most one
 * module loaded in the system.
 */
interface IWifi {
  /**
   * Requests notifications of significant events for the HAL. Multiple calls to
   * this must register multiple callbacks each of which must receive all
   * events. |IWifiEventCallback| object registration must be independent of the
   * state of the rest of the HAL and must persist though stops/starts. These
   * objects must be deleted when the corresponding client process is dead.
   *
   * @param callback An instance of the |IWifiEventCallback| HIDL interface
   *        object.
   * @return status WifiStatus of the operation.
   *         Possible status codes:
   *         |WifiStatusCode.SUCCESS|,
   *         |WifiStatusCode.UNKNOWN|
   */
  @entry
  @callflow(next={"*"})
  registerEventCallback(IWifiEventCallback callback)
      generates (WifiStatus status);

  /**
   * Get the current state of the HAL.
   *
   * @return started true if started, false otherwise.
   */
  isStarted() generates (bool started);

  /**
   * Perform any setup that is required to make use of the module. If the module
   * is already started then this must be a noop.
   * Must trigger |IWifiEventCallback.onStart| on success.
   *
   * @return status WifiStatus of the operation.
   *         Possible status codes:
   *         |WifiStatusCode.SUCCESS|,
   *         |WifiStatusCode.NOT_AVAILABLE|,
   *         |WifiStatusCode.UNKNOWN|
   */
  @entry
  @callflow(next={"registerEventCallback", "start", "stop", "getChip"})
  start() generates (WifiStatus status);

  /**
   * Tear down any state, ongoing commands, etc. If the module is already
   * stopped then this must be a noop. If the HAL is already stopped or it
   * succeeds then onStop must be called. After calling this all IWifiChip
   * objects will be considered invalid.
   * Must trigger |IWifiEventCallback.onStop| on success.
   * Must trigger |IWifiEventCallback.onFailure| on failure.
   *
   * Calling stop then start is a valid way of resetting state in the HAL,
   * driver, firmware.
   *
   * @return status WifiStatus of the operation.
   *         Possible status codes:
   *         |WifiStatusCode.SUCCESS|,
   *         |WifiStatusCode.NOT_STARTED|,
   *         |WifiStatusCode.UNKNOWN|
   */
  @exit
  @callflow(next={"registerEventCallback", "start", "stop"})
  stop() generates (WifiStatus status);

  ...
};
复制代码

WIFI HAL 总结

IWifi.hal文件位于hardware/interfaces/wifi/1.0/,这个取决于厂商使用的版本.前文在HalDeviceManager使用IWifi.getService();这样的方式来获取服务,那么我们就会很疑惑,服务注册在何处,代码在何方.

问题的关键呢就在这个hal文件中,它通过BP文件会生成JavaC++的模板代码,在bp文件中有不同的配置项配置它的生成代码.也可以自己通过命令去生成代码.

hidl-gen工具路径在代码路径下out/host/linux-x86/bin/路径下.可以通过hidl-gen -help命令查看对于参数.但是这个东西在android 10有被google抛弃,改用了AIDL方式.所以对这块就没有更深入的探究下去.

关于HIDL的官方介绍

简单的贴一下编译后的C++的注册代码

int main(int /*argc*/, char** argv) {
    android::base::InitLogging(
        argv, android::base::LogdLogger(android::base::SYSTEM));
    LOG(INFO) << "Wifi Hal is booting up...";

    configureRpcThreadpool(1, true /* callerWillJoin */);

    // Setup hwbinder service  这个是1_2版本的注册代码,不是上文中的1_0的版本,但是注册的地吗几乎是一致的,作为参考查看
    android::sp<android::hardware::wifi::V1_2::IWifi> service =
        new android::hardware::wifi::V1_2::implementation::Wifi(
            std::make_shared<WifiLegacyHal>(),
            std::make_shared<WifiModeController>(),
            std::make_shared<WifiFeatureFlags>());
    CHECK_EQ(service->registerAsService(), android::NO_ERROR)
        << "Failed to register wifi HAL";

    joinRpcThreadpool();

    LOG(INFO) << "Wifi Hal is terminating...";
    return 0;
}
复制代码

在这一块的参考资料确实有点少,理解也不够深入,举个例子

/**
 * Perform any setup that is required to make use of the module. If the module
 * is already started then this must be a noop.
 * Must trigger |IWifiEventCallback.onStart| on success.
 * 
 * @return status WifiStatus of the operation.
 *         Possible status codes:
 *         |WifiStatusCode.SUCCESS|,
 *         |WifiStatusCode.NOT_AVAILABLE|,
 *         |WifiStatusCode.UNKNOWN|
 */
android.hardware.wifi.V1_0.WifiStatus start()
    throws android.os.RemoteException;
复制代码

上面是tart()函数生成的JAvA代码,下文是生成的C++的代码,没有搞清楚在C++代码start函数中的
_hidl_cb参数从和而来,这个参数在后面的实际调用中也有用到.感兴趣的朋友,可以自己在深入的看下去,如果有答案了,有条件的话也告诉我一下.

/**
 * Perform any setup that is required to make use of the module. If the module
 * is already started then this must be a noop.
 * Must trigger |IWifiEventCallback.onStart| on success.
 * 
 * @return status WifiStatus of the operation.
 *         Possible status codes:
 *         |WifiStatusCode.SUCCESS|,
 *         |WifiStatusCode.NOT_AVAILABLE|,
 *         |WifiStatusCode.UNKNOWN|
 */
virtual ::android::hardware::Return<void> start(start_cb _hidl_cb) = 0;
复制代码

startSupplicant

在成功的通过HAL层代码启动wifi设备之后,代码就可以去调用了startSupplicant()函数,那么这个东西是干什么的,HAL层对外提供了start,stop,getChipIds,和注册回调函数等方案,其他并未提供,也就是说只有硬件相关的型号,和启动,暂停等关键函数,关于wifi的链接维护等函数并未提供,startSupplicant启动的就是wpa_sulicant.

wpa_sulicant

wpa_sulicant是一个开源项目,被google经过修改加入到Android系统当中.主要支持WPA,EAP,无线网卡和驱动.有兴趣致力于研究wifi相关的朋友可以研究一下这个东西,本文浅尝辄止.

wpa_sulicant官网

一个很不错的讲解网站

调用过程

startSupplicant

/** Helper method invoked to start supplicant if there were no STA ifaces */
private boolean startSupplicant() {
    synchronized (mLock) {
        if (!mIfaceMgr.hasAnyStaIface()) {
            //这里
            if (!mWificondControl.enableSupplicant()) {
                Log.e(TAG, "Failed to enable supplicant");
                return false;
            }
            if (!waitForSupplicantConnection()) {
                Log.e(TAG, "Failed to connect to supplicant");
                return false;
            }
            if (!mSupplicantStaIfaceHal.registerDeathHandler(
                    new SupplicantDeathHandlerInternal())) {
                Log.e(TAG, "Failed to register supplicant death handler");
                return false;
            }
        }
        return true;
    }
}


/**
* Enable wpa_supplicant via wificond.
* @return Returns true on success.
*/
public boolean enableSupplicant() {
    if (!retrieveWificondAndRegisterForDeath()) {
        return false;
    }
    try {
        //这里
        return mWificond.enableSupplicant();
    } catch (RemoteException e) {
        Log.e(TAG, "Failed to enable supplicant due to remote exception");
    }
    return false;
}
复制代码

可以看到最终启动wpa_supplicant使用一个wificond的一个对象.我们看下它是怎么获取的.

WifiInjector

private static final String WIFICOND_SERVICE_NAME = "wificond";

public IWificond makeWificond() {
    // We depend on being able to refresh our binder in WifiStateMachine, so don't cache it.
    IBinder binder = ServiceManager.getService(WIFICOND_SERVICE_NAME);
    return IWificond.Stub.asInterface(binder);
}
复制代码

也是通过binder的形式进行通信,那么wificond服务是何时注册到ServiceManager中的呢.接着往下看

wificond

include $(CLEAR_VARS)
LOCAL_MODULE := wificond
LOCAL_CPPFLAGS := $(wificond_cpp_flags)
LOCAL_INIT_RC := wificond.rc
LOCAL_C_INCLUDES := $(wificond_includes)
LOCAL_SRC_FILES := \
    main.cpp
LOCAL_SHARED_LIBRARIES := \
    android.hardware.wifi.offload@1.0 \
    libbinder \
    libbase \
    libcutils \
    libhidlbase \
    libhidltransport \
    libminijail \
    libutils \
    libwifi-system \
    libwifi-system-iface
LOCAL_STATIC_LIBRARIES := \
    libwificond
include $(BUILD_EXECUTABLE)
复制代码

这个是system/connectivity/wificond/android.mk中的一段,大意就是编译一个Natvie C的可执行文件,modulewificond.

在对应的main.cpp我们找到了注册服务的代码

int main(int argc, char** argv) {
  ...
  RegisterServiceOrCrash(server.get());

  event_dispatcher->Poll();
  LOG(INFO) << "wificond is about to exit";
  return 0;
}
复制代码
void RegisterServiceOrCrash(const android::sp<android::IBinder>& service) {
  android::sp<android::IServiceManager> sm = android::defaultServiceManager();
  CHECK_EQ(sm != NULL, true) << "Could not obtain IServiceManager";
   //这里addService到ServiceManager中
  CHECK_EQ(sm->addService(android::String16(kServiceName), service),
           android::NO_ERROR);
}
复制代码

那么它是在什么时候执行这个main.cpp呢,跟同事讨论这个问题时,猜测

  • 在开机流程中有一段代码是会执行所有服务的main()函数.(未经确认,我还没有仔细的梳理过开机流程).

不过我在init.zygote文件找到这样一段脚本文件

    onrestart restart audioserver
    onrestart restart cameraserver
    onrestart restart media
    onrestart restart netd
    onrestart restart wificond
复制代码

如果和推测一致呢就是在开机流程中已经执行了,在这里用restart确保其一定被启动.负责无法理解要用 restart去启动.好,解释了服务在何时注册,何时启动之后,我们接着往下看他是怎么启动wpa_supplicant.

Status Server::enableSupplicant(bool* success) {
    //这里,接着寻找 supplicant_manager_
  *success =  supplicant_manager_->StartSupplicant();
  return Status::ok();
}
复制代码

supplicant_manager

  • 代码路径 frameworks/opt/net/wifi/libwifi_system/supplicant_manager.cpp

在这里呢就简单的分析一下它的启动代码

namespace {

const char kSupplicantInitProperty[] = "init.svc.wpa_supplicant";
const char kSupplicantServiceName[] = "wpa_supplicant";

}  // namespace

bool SupplicantManager::StartSupplicant() {
  char supp_status[PROPERTY_VALUE_MAX] = {'\0'};
  int count = 200; /* wait at most 20 seconds for completion */
  const prop_info* pi;
  unsigned serial = 0;

  /* Check whether already running */
  //获取property属性 赋值给 supp_status ,字符串对比判断其是否已经启动,如果启动就
  //return 
  if (property_get(kSupplicantInitProperty, supp_status, NULL) &&
      strcmp(supp_status, "running") == 0) {
    return true;
  }

  /*
   * Get a reference to the status property, so we can distinguish
   * the case where it goes stopped => running => stopped (i.e.,
   * it start up, but fails right away) from the case in which
   * it starts in the stopped state and never manages to start
   * running at all.
   */
   //找到属性值索引
  pi = __system_property_find(kSupplicantInitProperty);
  if (pi != NULL) {
    //拿到其序号
    serial = __system_property_serial(pi);
  }
    
   //启动 服务
  property_set("ctl.start", kSupplicantServiceName);
  //让出自己的CPU控制权,将自己排到CPU序列队尾
  sched_yield();

  while (count-- > 0) {
    if (pi == NULL) {
      //空的话就重新获取获取一下索引
      pi = __system_property_find(kSupplicantInitProperty);
    }
    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);
        //对比字符串,如果已经在运行就 return
        if (strcmp(supp_status, "running") == 0) {
          return true;
        } else if (strcmp(supp_status, "stopped") == 0) {
          return false;
        }
      }
    }
    usleep(100000);
  }
  return false;
}
复制代码

理解上面的代码主要需要了解关于android系统中property相关的知识.例如ctl.start是启动服务等等.
这个百度一下就有很多相关的文章,就不贴链接了.

总结

接下来就不去写关于wifi链接和扫描等相关内容了,其代码量之大,平常需要改动的需求也是非常少的,有兴趣了解的根据梳理的流程自行查看.接下来我们画一个图,来表示整个流程中我们重点关注的模块,和其主要功能.

Android_Wifi_flow.jpg

闲聊一些

  • Java层主要优化方向可能放在开机流程,不过提前启动应该收效甚微。

  • 状态机优化,这个从Google不同版本的源代码就可以看出,Google自己在新的版本中也在不断的优化关于状态机的模块。 理解上层的逻辑代码也必须了解状态机。

  • hal层主要控制了硬件设备的启动和暂停。

  • wpa_supplicant,关注这个模块应该是深入了解和优化,也是一个开源项目。(留待以后深入研究。)

  • 后续的计划呢,可能重心要放在优化方向。关于binder的文章还要再退迟,不知道今年能不能完成了。

© 版权声明
THE END
喜欢就支持一下吧
点赞0 分享