Android P HIDL服務綁定模式與直通模式的分析 (原創)


  從Android 8.0之后,Android 引入Treble機制,主要是為了解決目前Android 版本之間升級麻煩的問題,將OEM適配的部分vendor與google 對android  大框架升級的部分system部分做了分離,一旦適配了一個版本的vendor信息之后,之后的版本再進行升級時,直接升級system即可,這個就不會給OEM廠商升級帶來太大的工作量,直接升級最新功能,可以解決目前市面上Android版本過來凌亂的問題。

  首先Treble機制在Vendor分區中有兩種模式,一個編譯成so庫,供System分區的進程和應用直接加載調用,兩者在同一個進程中,這種叫直通式 HAL(passthrough)模式, 另外一種是直接編譯成一個daemon可運行的服務,然后System分區的進程通過HwBinder的IPC通信方式來調用,兩者在二個獨立的進程中,這種稱為綁定式HAL(Binderized )模式。

HIDL 初始化

  • Passthrough
extern "C" IMapper* HIDL_FETCH_IMapper(const char* name){
  return CrallocLoader::load();
}
class GrallocLoader : public V2.0::passthrough::GrallocLoader {
    public:
      static IMapper* load(){
        const hw_module_t* module = loadModule();
        auto hal = createHal(module);
        return createMapper(std::move(hal));
      }

    static const hw_module_t loadModule(){
      const hw_module_t* module;
      int error = hw_get_module(GRALLOC_HARDWARE_MODULE_ID, &module);
      return module;
}
    
    static std::unique_prt<hal::MapperHal> createHal(const hw_module_t* module){
      auto hal = std::make_unique<GrallocHal>();
      return hal->initWithModule(module) ? std::move(hal) : nullptr;
    } }

 直通模式與 綁定模式最大的區別就是直通模式沒有一個獨立運行的服務進程,而綁定模式是作為一個獨立運行的服務相當於Deamon進程在運行。直通模式是將android 8.0之前的版本的module 封裝起來,供System以上的服務進行調用, 上層直接調用 HIDL_FETCH_XXX 來調用此接口的。

  • Binderized 

綁定式可以用兩種方式來綁定服務,第一種通過defaultPassthroughServiceImplementation 的調用來注冊服務,另外一種是直接調用RegisterAsService來注冊服務。

1. defaultPassthroughServiceImplementation 

int main{
  return defaultPassthroughServiceImplementation<IDemo>(4); //4表示與/dev/hwbinder通信的最大的線程數
}

這個函數的功能是創建默認的IDemo直通服務的實現方式。

1 status_t defaultPassthroughServiceImplementation(size_t maxThreads = 1){
2     configureRpcThreadpool(maxThreads, true); 3 status_t result = registerPassthroughServiceImplementation<IDemo>("default");
     joinRpcThreadpool();
    return UNKOWN_ERROR; 4 }
status_t registerPassthroughServiceImplementation(String name = "default"){
  sp<IDemo> service = Interface::getService(name, true);
  ....
  status_t status = service->registerAsService(name);
  return status; }

 

將IDemo 實例化一個對象出來, 然后將其與“default”為名稱注冊到一個服務上。那我們來看RegisterAsService,這個函數是使用hidl的工具生成的:

out/soong/.intermediates/hardware/interfaces/demo/1.0/android.hardware.demo@1.0_genc++/gen/android/hardware/demo/1.0/DemoAll.cpp
::android::status_t IDemo::registerAsService(const std::string &serviceName){
  ::android::hardware::details::onRegistration("android.hardware.demo@1.0", "IDemo", serviceName);

  const ::android::sp<::android::hidl::manager::V1_0::IServiceManager> sm = ::android::hardware::defaultServiceManager();
::android::hardware::Return<bool> ret = sm->add(serviceName.c_str(), this);
  return ret.isOk() && ret ? ::android::OK :: android::UNKNOWN_ERROR;
}

 這里就是將IDemo這個實例 通過IServicemanager的add 接口,添加到服務列表中, 到目前前止,就將defaultPassthroughServiceImplementation 的服務注冊過程完成了。

2. RegisterAsService 

int main(){
    configureRpcThreadpool(4, true);
    Demo mDemo = new Demo();
    mDemo.registerAsService();
    joinRpcThreadpool();
    return 1;
}

 這里核心的也是registerAsService(), 此函數之前已經分析過了,就不重復解釋了,就是注冊一個服務到Service列表中。 

 HIDL的調用

上面已經說明了直通模式與綁定模式的初始化,現在我們來介紹下如何調用直通模式和綁定模式,兩者調用的函數有沒有什么區別呢? 答案是兩者使用的調用接口是一樣的,都是通過getService 來調用的。

IMapper mMapper = mapper::V2_0::IMapper::getService()

 同樣getService 也是HIDL的工具生成的函數執行的:

out/soong/.intermediates/hardware/interfaces/graphics/mapper/2.1/android.hardware.graphics.mapper@2.1_genc++/gen/android/hardware/graphics/mapper/2.1/MapperAll.cpp
::android::sp<IMapper> IMapper::getService(const std::string &serviceName, const bool getStub){
  return ::android::hardware::details::getServiceInternal<BpHwMapper>(serviceName, true, getStub);
}
sp<IType> getServiceInternal(const std::string & instance, bool retry, bool getStub){
    sp<IBase> base = getRawServiceInternal(IType::descriptor, inst/ance, retyr, getStub);
.....
}

sp<::android::hidl::base::V1_0::IBase> getRawServiceInternal(const std::string& descriptor,
                                                             const std::string& instance,
                                                             bool retry, bool getStub) {
    const sp<IServiceManager1_1> sm = defaultServiceManager1_1();
....
//綁定模式
    const bool vintfHwbinder = (transport == Transport::HWBINDER);
//直通模式
    const bool vintfPassthru = (transport == Transport::PASSTHROUGH);
......
//綁定模式
    for (int tries = 0; !getStub && (vintfHwbinder || vintfLegacy); tries++) {
        Return<sp<IBase>> ret = sm->get(descriptor, instance);
    }
//直通模式
    if (getStub || vintfPassthru || vintfLegacy) {
        const sp<IServiceManager> pm = getPassthroughServiceManager();
            sp<IBase> base = pm->get(descriptor, instance).withDefault(nullptr);
    }

....

 這里分成了二條路,綁定模式就直接通過IServiceManager->get的方法去獲取,是不是覺得很熟悉,就和AIDL一個套路,通過BpBinder 和 BnBinder 直接對Binder驅動的操作來完成數據交換,但AIDL的binder驅動是/dev/binder, HIDL的hwbinder驅動是/dev/hwbinder,具體的我們就不去詳細分析了。關於直通模式,先通過getPassthroughServiceManager ,獲取IServiceManager的句柄,然后再get得到對應的服務。

sp<IServiceManager1_0> getPassthroughServiceManager() {
    return getPassthroughServiceManager1_1();
}
sp<IServiceManager1_1> getPassthroughServiceManager1_1() {
    static sp<PassthroughServiceManager> manager(new PassthroughServiceManager());
    return manager;
}

這里相當於直接new了一個PassthroughServiceManager(), 之后再調用get 方法:

     static void openLibs(
        const std::string& fqName,
        const std::function<bool /* continue */ (void* /* handle */, const std::string& /* lib */,
                                                 const std::string& /* sym */)>& eachLib) {
        std::string packageAndVersion = fqName.substr(0, idx);
        std::string ifaceName = fqName.substr(idx + strlen("::"));

        const std::string prefix = packageAndVersion + "-impl";
     //HIDL_FETCH_XXX 出現了,就是passthrough模式下需要被調用的方法。
const std::string sym = "HIDL_FETCH_" + ifaceName; ..... std::vector<std::string> paths = {HAL_LIBRARY_PATH_ODM, HAL_LIBRARY_PATH_VENDOR, halLibPathVndkSp, HAL_LIBRARY_PATH_SYSTEM}; .... for (const std::string& path : paths) { std::vector<std::string> libs = search(path, prefix, ".so"); for (const std::string &lib : libs) {
          //路徑最后組裝成/system 或者/vendor 下面的供調用的xxxxx-impl.so
const std::string fullPath = path + lib; if (path == HAL_LIBRARY_PATH_SYSTEM) {
//這里就供dlopen了。 handle
= dlopen(fullPath.c_str(), dlMode); } else { handle = android_load_sphal_library(fullPath.c_str(), dlMode); } ..... } } Return<sp<IBase>> get(const hidl_string& fqName, const hidl_string& name) override { sp<IBase> ret = nullptr; openLibs(fqName, [&](void* handle, const std::string &lib, const std::string &sym) { IBase* (*generator)(const char* name);
//這里就會調用 到sym 也就是HIDL_FETCH_XXX ,然后通過dlsym 去鏈接調用了。
*(void **)(&generator) = dlsym(handle, sym.c_str()); ret = (*generator)(name.c_str()); }); return ret; }

到這里就可以看到HIDL_FETCH_XXX的調用過程了。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM