美文网首页Android系统研究
Radio Bringup(App-Framework-Hal)

Radio Bringup(App-Framework-Hal)

作者: 波雅_Z | 来源:发表于2020-01-20 14:40 被阅读0次

    简介

    Radio 的逻辑其实很简单,但是bringup的过程涉及到了从app到hal的整个框架交互,是一个很好的入手HAL的机会,这里以bringup的角度,做了一次简单的代码走读,后续会陆续更新,Radio 的工作流程,常用术语,HIDL机制等。

    结构图

    image.png

    Bringup流程

    对外接口在framewrok的RadioManager和TunerAdapter实现。
    LINUX/android/frameworks/base/core/java/android/hardware/radio/RadioManager.java
    LINUX/android/frameworks/base/core/java/android/hardware/radio/TunerAdapter.java

    并依赖回调异步返回请求的数据。

    LINUX/android/frameworks/base/core/java/android/hardware/radio/TunerCallbackAdapter.java

    RadioManager通过AIDL调用Radio跑在framework的系统服务。

    关于AIDL的实现方法和实现原理后续赘述,这里整理下调查之后的系统服务的实现过程。
    RadioManager中找到service是通过:

    mService = IRadioService.Stub.asInterface(
            ServiceManager.getServiceOrThrow(Context.RADIO_SERVICE));
    

    这里的ServiceManager的实现:
    LINUX/android/frameworks/base/core/java/android/os/ServiceManager.java

    public static IBinder getServiceOrThrow(String name) throws ServiceNotFoundException {
        final IBinder binder = getService(name);
        if (binder != null) {
            return binder;
        } else {
            throw new ServiceNotFoundException(name);
        }
    }
    
    public static IBinder getService(String name) {
        try {
            IBinder service = sCache.get(name);
            if (service != null) {
                return service;
            } else {
                return Binder.allowBlocking(getIServiceManager().getService(name));
            }
        } catch (RemoteException e) {
            Log.e(TAG, "error in getService", e);
        }
        return null;
    }
    

    这里不太好深究具体走的是哪条路,是从sCache取到的,还是从IServiceManager取到的,所以决定从RADIO_SERVICE这个关键字入手。
    从全局的搜索结果来看,在BroadcastRadioService中注册的该服务.

    LINUX/android/frameworks/base/services/core/java/com/android/server/broadcastradio/BroadcastRadioService.java

    @Override
    public void onStart() {
        publishBinderService(Context.RADIO_SERVICE, mServiceImpl);
    }
    

    LINUX/android/frameworks/base/services/core/java/com/android/server/SystemService.java

    protected final void publishBinderService(String name, IBinder service,
            boolean allowIsolated) {
        ServiceManager.addService(name, service, allowIsolated);
    }
    
    public static void addService(String name, IBinder service, boolean allowIsolated) {
        try {
            getIServiceManager().addService(name, service, allowIsolated);
        } catch (RemoteException e) {
            Log.e(TAG, "error in addService", e);
        }
    }
    

    LINUX/android/frameworks/base/core/java/android/os/ServiceManager.java

    public static void addService(String name, IBinder service, boolean allowIsolated) {
        try {
            getIServiceManager().addService(name, service, allowIsolated);
        } catch (RemoteException e) {
            Log.e(TAG, "error in addService", e);
        }
    }
    

    可以看到这里IServiceManager 将RADIO_SERVICE add进去了,在前面的getService的时候自然可以从IServiceManager中get到。

    按理说调查到这里,整个接口都是完整的,我们的服务可以取到并正常运行了,但是事实证明RadioManager中一直取不到Radio的服务,编译未报错,
    运行报错。
    所以说该服务并没有起来,继而开始调查系统服务的启动过程。

    既然是一个服务,要启动,无非是startService或者是bind,这里的系统服务,调用startService的可能性比较大,搜索类名,得到:

    LINUX/android/frameworks/base/services/java/com/android/server/SystemServer.java

    private void startOtherServices() {
    ......
    if (mPackageManager.hasSystemFeature(PackageManager.FEATURE_BROADCAST_RADIO)) {
    traceBeginAndSlog("StartBroadcastRadioService");
    mSystemServiceManager.startService(BroadcastRadioService.class);
    traceEnd();
    }
    ......
    }
    

    而startOtherServices()的调用在SystemServer的run()方法中:

    public static void main(String[] args) {
        new SystemServer().run();
    }
    
    private void run() {
    ......
    startOtherServices()
    ......
    }
    

    而具体SystemServer的启动时机,我没有找到,网上搜索来看,是由一个rc文件拉起来的。

    [https://blog.csdn.net/user11223344abc/article/details/80625185]

    Android系统是基于Linux内核的,而在Linux系统中,所有的进程都是init进程的子孙进程,也就是说,
    所有的进程都是直接或者间接地由init进程fork出来的。Zygote进程也不例外,它是在系统启动的过程,由init进程创建的。
    在系统启动脚本system/core/rootdir/init.rc文件中。—上帝init.rc

    系统启动时init进程会创建Zygote进程(准确的说是通过app_main.cpp启动的Zygote进程),
    Zygote进程负责后续Android应用程序框架层的其它进程的创建和启动工作。—android大boss,孵化进程
    Zygote进程会首先创建一个SystemServer进程,SystemServer进程负责启动系统的关键服务,如AMS,PMS等。—android大boss首席小弟

    后续会在开机启动优化的文档中详细跟踪流程。

    也就是说开机启动SystemServer进程,从而把framewrok的系统服务拉起来,这里既然没起来,必然是判断没有走过,那这个判断又是个什么东西。。。

    mPackageManager.hasSystemFeature(PackageManager.FEATURE_BROADCAST_RADIO)
    
    /**
    * Feature for {@link #getSystemAvailableFeatures} and
    * {@link #hasSystemFeature}: The device includes broadcast radio tuner.
    * @hide
    */
    @SystemApi
    @SdkConstant(SdkConstantType.FEATURE)
    public static final String FEATURE_BROADCAST_RADIO = "android.hardware.broadcastradio";
    

    从定义来看,是配置的系统是否支持Radio,也就是取决于硬件是否支持,当前不支持说明没有配置,那要怎么配置嘞。

    跟踪代码一步步来看:
    LINUX/android/frameworks/base/core/java/android/app/ApplicationPackageManager.java

    @Override
    public boolean hasSystemFeature(String name, int version) {
        try {
            return mPM.hasSystemFeature(name, version);
        } catch (RemoteException e) {
            throw e.rethrowFromSystemServer();
        }
    }
    

    LINUX/android/frameworks/base/services/core/java/com/android/server/pm/PackageManagerService.java

    @Override
    public boolean hasSystemFeature(String name, int version) {
        // allow instant applications
        synchronized (mAvailableFeatures) {
            final FeatureInfo feat = mAvailableFeatures.get(name);
            if (feat == null) {
                return false;
            } else {
                return feat.version >= version;
            }
        }
    }
    
    mAvailableFeatures = systemConfig.getAvailableFeatures();
    

    LINUX/android/frameworks/base/core/java/com/android/server/SystemConfig.java

    public ArrayMap<String, FeatureInfo> getAvailableFeatures() {
        return mAvailableFeatures;
    }
    
    private void addFeature(String name, int version) {
        FeatureInfo fi = mAvailableFeatures.get(name);
        if (fi == null) {
            fi = new FeatureInfo();
            fi.name = name;
            fi.version = version;
            mAvailableFeatures.put(name, fi);
        } else {
            fi.version = Math.max(fi.version, version);
        }
    }
    
    private void readPermissionsFromXml(File permFile, int permissionFlag) {
    FileReader permReader = null;
    try {
        permReader = new FileReader(permFile);
    } catch (FileNotFoundException e) {
        Slog.w(TAG, "Couldn't find or open permissions file " + permFile);
        return;
    }
    
    final boolean lowRam = ActivityManager.isLowRamDeviceStatic();
    
    try {
        XmlPullParser parser = Xml.newPullParser();
        parser.setInput(permReader);
    ......
    addFeature(fname, fversion);
    ......
    }
    
    void readPermissions(File libraryDir, int permissionFlag) {
        // Read permissions from given directory.
        if (!libraryDir.exists() || !libraryDir.isDirectory()) {
            if (permissionFlag == ALLOW_ALL) {
                Slog.w(TAG, "No directory " + libraryDir + ", skipping");
            }
            return;
        }
        if (!libraryDir.canRead()) {
            Slog.w(TAG, "Directory " + libraryDir + " cannot be read");
            return;
        }
    
        // Iterate over the files in the directory and scan .xml files
        File platformFile = null;
        for (File f : libraryDir.listFiles()) {
            // We'll read platform.xml last
            if (f.getPath().endsWith("etc/permissions/platform.xml")) {
                platformFile = f;
                continue;
            }
    
            if (!f.getPath().endsWith(".xml")) {
                Slog.i(TAG, "Non-xml file " + f + " in " + libraryDir + " directory, ignoring");
                continue;
            }
            if (!f.canRead()) {
                Slog.w(TAG, "Permissions library file " + f + " cannot be read");
                continue;
            }
    
            readPermissionsFromXml(f, permissionFlag);
        }
    
        // Read platform permissions last so it will take precedence
        if (platformFile != null) {
            readPermissionsFromXml(platformFile, permissionFlag);
        }
    }
    
    SystemConfig() {
        // Read configuration from system
        readPermissions(Environment.buildPath(
                Environment.getRootDirectory(), "etc", "sysconfig"), ALLOW_ALL);
        // Read configuration from the old permissions dir
        readPermissions(Environment.buildPath(
                Environment.getRootDirectory(), "etc", "permissions"), ALLOW_ALL);
        // Allow Vendor to customize system configs around libs, features, permissions and apps
        int vendorPermissionFlag = ALLOW_LIBS | ALLOW_FEATURES | ALLOW_PERMISSIONS |
                ALLOW_APP_CONFIGS;
        readPermissions(Environment.buildPath(
                Environment.getVendorDirectory(), "etc", "sysconfig"), vendorPermissionFlag);
        readPermissions(Environment.buildPath(
                Environment.getVendorDirectory(), "etc", "permissions"), vendorPermissionFlag);
        // Allow ODM to customize system configs around libs, features and apps
        int odmPermissionFlag = ALLOW_LIBS | ALLOW_FEATURES | ALLOW_APP_CONFIGS;
        readPermissions(Environment.buildPath(
                Environment.getOdmDirectory(), "etc", "sysconfig"), odmPermissionFlag);
        readPermissions(Environment.buildPath(
                Environment.getOdmDirectory(), "etc", "permissions"), odmPermissionFlag);
        // Only allow OEM to customize features
        readPermissions(Environment.buildPath(
                Environment.getOemDirectory(), "etc", "sysconfig"), ALLOW_FEATURES);
        readPermissions(Environment.buildPath(
                Environment.getOemDirectory(), "etc", "permissions"), ALLOW_FEATURES);
        //Remove vulkan specific features
        if (SystemProperties.getBoolean("persist.graphics.vulkan.disable", false)) {
            removeFeature(PackageManager.FEATURE_VULKAN_HARDWARE_LEVEL);
            removeFeature(PackageManager.FEATURE_VULKAN_HARDWARE_VERSION);
            removeFeature(PackageManager.FEATURE_VULKAN_HARDWARE_COMPUTE);
        }
    }
    

    LINUX/android/frameworks/base/core/java/android/os/Environment.java

    public static File getRootDirectory() {
        return DIR_ANDROID_ROOT;
    }
    
    private static final File DIR_ANDROID_ROOT = getDirectory(ENV_ANDROID_ROOT, "/system");
    private static final String ENV_ANDROID_ROOT = "ANDROID_ROOT";
    static File getDirectory(String variableName, String defaultPath) {
        String path = System.getenv(variableName);
        return path == null ? new File(defaultPath) : new File(path);
    }
    

    可以看出来,该权限是在system/etc/permissions下边的xml配置文件中进行的配置。
    后续调查发现,一般是从framework/base/data/etc/xxx.xm复制到该位置。

    源码中来看存在

    framework/base/data/etc/android.hardware.broadcastradio.xml

    在device的配置中将该文件copy到指定位置

    PRODUCT_COPY_FILES += \
        frameworks/native/data/etc/android.hardware.broadcastradio.xml:vendor/etc/permissions/android.hardware.broadcastradio.xml \
    

    运行,服务正常启动。
    总的来说,开机init.rc文件拉起SystemServer,对应的服务自己注册,在SystemServer中被拉起来。

    至此,Radio Framework的系统服务调查结束,开始向HIDL和HAL深入。



    调用到达framewrok后,进入JNI:

    LINUX/android/frameworks/base/services/core/jni/BroadcastRadio
    BroadcastRadioService.cpp
    TunerCallback.cpp
    Tuner.cpp
    NativeCallbackThread.cpp
    regions.cpp
    JavaRef.cpp
    convert.cpp
    types.h

    JNI部分的向下调用通过HIDL,关于HIDL的实现方法和实现原理后续赘述:

    LINUX/android/frameworks/base/services/core/jni/BroadcastRadio/BroadcastRadioService.cpp

    static jobject nativeLoadModules(JNIEnv *env, jobject obj, jlong nativeContext) {
    ......
    auto& ctx = getNativeContext(nativeContext);
    // Get list of registered HIDL HAL implementations.
    auto manager = hardware::defaultServiceManager();
    if (manager == nullptr) {
        ALOGE("Can't reach service manager, using default service implementation only");
        services = std::vector<hidl_string>({ "default" });
    } else {
        manager->listByInterface(V1_0::IBroadcastRadioFactory::descriptor,
                [&services](const hidl_vec<hidl_string> &registered) {
            services = registered;
        });
    }
    ......
    }
    

    这里的defaultServiceManager应该是相当于上面AIDL的ServiceManager.
    通过listByInterface来获取service,这里看到入参有两个,第一个是HAL服务注册的名字,第二个是返回该注册对应的服务列表,我们一步一步来看是怎么通过名字来获得服务的,而这个名字又是在哪里传进来的。

    系统中关于HIDL的代码一般会放在:
    hardware/interfaces 下面,

    radio hidl的接口代码位置--
    hardware/interfaces/broadcastradio/
    Android比较新的版本都在推Android.bp,后续会详细介绍Android.bp和HAL的相关结构情况,这里继续针对bringup的流程来介绍。

    当时radio还停留在1的版本迭代,在Android P升级到2的版本并开始从HAL的service直接和framework层交互。
    除掉test的相关代码后,代码结构如下:

    ├── 1.0
    │   ├── Android.bp
    │   ├── default
    │   │   ├── Android.bp
    │   │   ├── BroadcastRadio.cpp
    │   │   ├── BroadcastRadioFactory.cpp
    │   │   ├── BroadcastRadioFactory.h
    │   │   ├── BroadcastRadio.h
    │   │   ├── OWNERS
    │   │   ├── Tuner.cpp
    │   │   ├── Tuner.h
    │   │   ├── Utils.cpp
    │   │   └── Utils.h
    │   ├── IBroadcastRadioFactory.hal
    │   ├── IBroadcastRadio.hal
    │   ├── ITunerCallback.hal
    │   ├── ITuner.hal
    │   └── types.hal
    ├── 1.1
    │   ├── Android.bp
    │   ├── default
    │   │   ├── Android.bp
    │   │   ├── android.hardware.broadcastradio@1.1-service.rc
    │   │   ├── BroadcastRadio.cpp
    │   │   ├── BroadcastRadioFactory.cpp
    │   │   ├── BroadcastRadioFactory.h
    │   │   ├── BroadcastRadio.h
    │   │   ├── OWNERS
    │   │   ├── resources.h
    │   │   ├── service.cpp
    │   │   ├── Tuner.cpp
    │   │   ├── Tuner.h
    │   │   ├── VirtualProgram.cpp
    │   │   ├── VirtualProgram.h
    │   │   ├── VirtualRadio.cpp
    │   │   └── VirtualRadio.h
    │   ├── IBroadcastRadioFactory.hal
    │   ├── IBroadcastRadio.hal
    │   ├── ITunerCallback.hal
    │   ├── ITuner.hal
    │   ├── tests
    │   │   ├── Android.bp
    │   │   ├── OWNERS
    │   │   └── WorkerThread_test.cpp
    │   ├── types.hal
    │   └── utils
    │       ├── Android.bp
    │       ├── include
    │       │   └── broadcastradio-utils
    │       │       ├── Utils.h
    │       │       └── WorkerThread.h
    │       ├── OWNERS
    │       ├── Utils.cpp
    │       └── WorkerThread.cpp
    │  
    └── Android.bp
    

    首先最上层的类中包含的XXX.hal的文件与AIDL的XXX.aidl类似,是HIDL的接口文件,会编译出针对C++调用者使用的h和cpp文件以及针对java调用者使用的java文件,从jni的代码中调用的是cpp文件。

    编译出的文件可以在

    out/soong/.intermediates/hardware/interfaces/broadcastradio/1.0/android.hardware.broadcastradio@1.0_genc++/gen/android/hardware/broadcastradio/1.0/

    下找到。

    BroadcastRadioFactoryAll.cpp中可以看到
    namespace为

    android :: hardware::broadcastradio::V1_0

    上面提到的descriptor为:

    const char* IBroadcastRadioFaactory::descriptor("android.hardware.broadcastradio@1.0::IBroadcastRadioFactory");
    

    尝试编译1.1的接口后就可以发现同样的out目录下的1.1路径下

    out/soong/.intermediates/hardware/interfaces/broadcastradio/1.1/android.hardware.broadcastradio@1.1_genc++/gen/android/hardware/broadcastradio/1.1

    也会生成新的文件
    BroadcastRadioFactoryAll.cpp中可以看到

    namespace为

    android :: hardware::broadcastradio::V1_1

    上面提到的descriptor为:

    const char* IBroadcastRadioFaactory::descriptor("android.hardware.broadcastradio@1.1::IBroadcastRadioFactory");
    

    而第二个入参registered,传的是一个引用,用来存储我们要用的service。

    /system/hwservicemanager/ServiceManager.cpp

    Return<void> ServiceManager::listByInterface(const hidl_string& fqName,
                                                listByInterface_cb _hidl_cb) {
        ......
        auto ifaceIt = mServiceMap.find(fqName);
        ......
        const auto &instanceMap = ifaceIt->second.getInstanceMap();
        ......
        for (const auto &serviceMapping : instanceMap) {
            const std::unique_ptr<HidlService> &service = serviceMapping.second;
            if (service->getService() == nullptr) continue;
            list[idx++] = service->getInstanceName();
        }
        ......
    }
    

    /system/hwservicemanager/ServiceManager.h

    std::map<
            std::string, // package::interface e.x. "android.hidl.manager@1.0::IServiceManager"
            PackageInterfaceMap
        > mServiceMap;
    

    从mServiceMap取到的interface的Map,而mServiceMap的初始化

    /system/hwservicemanager/ServiceManager.cpp

    Return<bool> ServiceManager::add(const hidl_string& name, const sp<IBase>& service) {
        ......
        auto ret = service->interfaceChain([&](const auto &interfaceChain) {
            ......(删除注册的default service)
            for(size_t i = 0; i < interfaceChain.size(); i++) {
                const std::string fqName = interfaceChain[i];
                PackageInterfaceMap &ifaceMap = mServiceMap[fqName];
                HidlService *hidlService = ifaceMap.lookup(name);
                if (hidlService == nullptr) {
                    ifaceMap.insertService(
                        std::make_unique<HidlService>(fqName, name, service, pid));
                } else {
                    hidlService->setService(service, pid);
                }    
            }
            ......
        }
        ....
    }
    

    可以看到是在注册service的时候,通过service的InterfaceChain拿到,而该值的定义,可以在刚刚HIDL 的interface编译出的路径下找到:

    out/soong/.intermediates/hardware/interfaces/broadcastradio/1.1/android.hardware.broadcastradio@1.1_genc++/gen/android/hardware/broadcastradio/1.1

    BroadcastRadioFactoryAll.cpp中可以看到

    ::android::hardware::Return<void> IBroadcastRadioFactory::interfaceChain(interfaceChain_cb _hidl_cb){
        _hidl_cb({
            IBroadcastRadioFactory::descriptor,
            ::android::harware::broadcastradio::V1_0::IBroadcastRadioFactory::descriptor,
            ::android::hidl::base::V1_0::IBase::descriptor,
        });
        return ::android::hardware::Void();
    }
    

    也就是说1.1的service中Chain会包含它的父类V1_0的descriptor和V1_0的父类Base的descriptor.
    则给第二个入参赋值的service是通过遍历这个interfaceChain,并检查对应的service是否已经注册,然后返回的。

    接下来,我们回到radio的service。
    在1.1版本存在的情况下,这里应该会拿到1.1的service.通过castFrom来获取各个类并调用接口。

    LINUX/android/frameworks/base/services/core/jni/BroadcastRadio/BroadcastRadioService.cpp

    详细的接口调用我们这里不再赘述,还是回到loadmodule==>
    首先要获取到BroadcastRadio--

    factory->connectModule(clazz, [&](Result res, const sp<V1_0::IBroadcastRadio>& module) {
    if (res == Result::OK) {
      module10 = module;
      module11 = V1_1::IBroadcastRadio::castFrom(module).withDefault(nullptr);
    } else if (res != Result::INVALID_ARGUMENTS) {
      ALOGE("couldn't load %s:%s module",
      serviceName.c_str(), V1_0::toString(clazz).c_str());
    }
    });
    

    若当前为1.1的服务,通过castFrom获得,同理可得1.2的服务。

    相关配置获取--

    if (module11 != nullptr) {
      hidlResult = module11->getProperties_1_1([&](const V1_1::Properties& properties) {
      nModule.bands = properties.base.bands;
      JModule = convert::ModulePropertiesFromHal(env, properties, idx, serviceName);
    });
    }
    

    可以看到HAL的对象转换为jni对象都是在convert中做的,详细的转化过程可以自行在

    frameworks/base/services/core/jni/BroadcastRadio/convert.cpp

    中查看。
    流程走到这里,如果debug都顺利应该就说明与HAL 的服务交互正常,然而,事情怎会如此简单~~

    这里并没有拿到HAL 的service,从adb shell ps -A 的情况来看,radio hal的service也确实没有被拉起来。

    回到HAL 的HIDL这里,我们来继续确认service到底是什么原因没有被拉起来。
    拿1.1的service来看,service被添加的HAL的服务管理是在service.cpp

    /hardware/interfaces/broadcastradio/1.1/default/service.cpp

    int main(int /* argc */, char** /* argv */) {
    ......
    auto status = broadcastRadioFactory.registerAsService();
    ......
    }
    

    而该服务确实是被编译进可执行文件的:

    /hardware/interfaces/broadcastradio/1.1/default/Android.bp

    cc_binary {
    name: "android.hardware.broadcastradio@1.1-service",
    init_rc: ["android.hardware.broadcastradio@1.1-service.rc"],
    vendor: true,
    relative_install_path: "hw",
    ......
    srcs: [
    ......
    "service.cpp"
    ],
    ......
    }
    


    所以下一步是进到设备中查看,编译出来的rc文件和可执行文件是否存在。

    从上面的Android.bp可以看到是编译在vendor下
    adb shell进入设备,在vendor/etc/init下是存放各个模块的rc文件,从而可以做到在开机启动的时候执行该rc,可以拉起该服务,radio的rc文件不存在。

    在vendor/bin/hw/下面去寻找android.hardware.broadcastradio@1.1-service,也不存在。

    可以预感到在device下面并没有将相关的模块编译引入。

    在device下引入相关模块:

    PRODUCT_PACKAGES +=
    vendor.ts.hardware.broadcastradio@1.1-service

    并添加相关的SELinux的权限处理和HAL的服务注册:
    SELinux的权限添加,可能不全,需要根据具体log情况配置--

    file_contexts
    /(vendor|system/vendor)/bin/hw/vendor\.ts\.broadcastradio@1\.1-service u:object_r:hal_broadcastradio_default_exec:s0
    
    hal_broadcastradio_default.te
    allow hal_broadcastradio_default system_server:binder call;
    
    platform_app.te
    allow platform_app broadcastradio_service:service_manager find;
    

    HAL的服务注册--

    device下有一个manifest.xml,添加radio 服务的相关配置

    <hal format="hidl">
    <name>android.hardware.broadcastradio</name>
    <transport>hwbinder</transport>
    <impl level="generic"></impl>
    <version>1.1</version>
    <interface>
    <name>IBroadcastRadioFactory</name>
    <instance>default</instance>
    </interface>
    </hal>
    

    添加后,编译image,烧录,重启,HAL的服务被正常拉起。

    此时如果已经安装了radio的apk,会发现已经可以成功拉起,并且出现可用电台,但是出现的电台并非是我们想要的实时电台,而大多是虚拟电台。从原生1.1的代码来看,HIDL的default实现中添加了很多模拟数据。

    │   │   ├── VirtualProgram.cpp
    │   │   ├── VirtualProgram.h
    │   │   ├── VirtualRadio.cpp
    │   │   └── VirtualRadio.h
    

    所以需要参考1.0的实现来完善1.1同底层的交互,当然也可以直接采用1.1的版本。不管是哪个版本,我们都来看下HIDL在这里是怎样实现与HAL层的通信的。

    从之前jni的逻辑来看,初步进行接口调用的是BroadcastRadio,这里我们来看看相关内容应该是在BroadcastRadio.cpp中进行初始化的。

    /hardware/interfaces/broadcastradio/1.0/default/BroadcastRadio.cpp

    void BroadcastRadio::onFirstRef() {
    ......
    const hw_module_t *mod;
    const char *classString = Utils::getClassString(mClassId);
    rc = hw_get_module_by_class(RADIO_HARDWARE_MODULE_ID, classString, &mod);
    }
    

    /hardware/libhardware/include/hardware/hardware.h

    /**
    * Every hardware module must have a data structure named HAL_MODULE_INFO_SYM
    * and the fields of this data structure must begin with hw_module_t
    * followed by module specific information.
    */
    typedef struct hw_module_t {
    uint32_t tag;
    uint16_t module_api_version;
    ......
    }hw_module_t;  
    

    可以看到hw_module_t结构体的声明在hardware.h,是对hardware module 的强制约束,意味着大部分的HAL hardware module 都是通过hw_get_module_by_class这种方式来做初步加载的。

    /hardware/libhardware/hardware.c

    int hw_get_module_by_class(const char *class_id, const char *inst, const struct hw_module_t **module) {
    int i = 0;
    char prop[PATH_MAX] = {0};
    char path[PATH_MAX] = {0};
    char name[PATH_MAX] = {0};
    char prop_name[PATH_MAX] = {0};
    if (inst)
    snprintf(name, PATH_MAX, "%s.%s", class_id, inst);
    else
    strlcpy(name, class_id, PATH_MAX);
    }
    

    从radio传入的入参可以知道name的值为radio.fm

    /hardware/libhardware/include/hardware/radio.h

    #define RADIO_HARDWARE_MODULE_ID "radio"

    /frameworks/base/services/core/jni/BroadcastRadio/BroadcastRadioService.cpp

    const std::vector<Class> gAllClasses = {
    Class::AM_FM,
    Class::SAT,
    Class::DT,
    }
    

    /hardware/interfaces/broadcastradio/1.0/types.hal

    enum Class : uint32_t {
    /** FM (including HD radio) and AM */
    AM_FM = 0,
    /** Satellite Radio */
    SAT = 1,
    /** Digital Radio (DAB) */
    DT = 2,
    }
    

    /hardware/interfaces/broadcastradio/1.0/default/Utils.cpp

    const char *Utils::sClassModuleNames[] = {
    RADIO_HARDWARE_MODULE_ID_FM, /* corresponds to RADIO_CLASS_AM_FM */ 
    RADIO_HARDWARE_MODULE_ID_SAT, /* corresponds to RADIO_CLASS_SAT */ 
    RADIO_HARDWARE_MODULE_ID_DT, /* corresponds to RADIO_CLASS_DT */
    }
    

    /hardware/libhardware/include/hardware/radio.h

    #define RADIO_HARDWARE_MODULE_ID_FM "fm" /* corresponds to RADIO_CLASS_AM_FM */
    #define RADIO_HARDWARE_MODULE_ID_SAT "sat" /* corresponds to RADIO_CLASS_SAT */ 
    #define RADIO_HARDWARE_MODULE_ID_DT "dt" /* corresponds to RADIO_CLASS_DT */
    

    本来是对三个class做了一个循环,但是因为我们只针对AM_FM做了module,所以其他的class值虽然也会传入,但是都会load失败,这里只看fm。
    继续回到刚才的hw_get_module_by_class,是从三个条件来匹配相关的库是否存在的。

    1. /hardware/libhardware/hardware.c
    int hw_get_module_by_class(const char *class_id, const char *inst, 
    const struct hw_module_t **module) {
    ......
    snprintf(prop_name, sizeof(prop_name), "ro.hardware.%s", name);
    if (property_get(prop_name, prop, NULL) > 0) {
    if (hw_module_exists(path, sizeof(path), name, prop) == 0) {
    goto found;
    } 
    }
    ......
    }
    

    获取当前ro.hardware.radio.fm的值,比如ro.hardware.camera=v4l2

    1. /hardware/libhardware/hardware.c
    static const char *variant_keys[] = {
    "ro.hardware", /* This goes first so that it can pick up a different file on the emulator. */
    "ro.product.board",
    "ro.board.platform",
    "ro.arch"
    };
    int hw_get_module_by_class(const char *class_id, const char *inst,
    const struct hw_module_t **module) {
    ......
    /* Loop through the configuration variants looking for a module */
    for (i=0 ; i<HAL_VARIANT_KEYS_COUNT; i++) {
    if (property_get(variant_keys[i], prop, NULL) == 0) {
    continue;
    }
    if (hw_module_exists(path, sizeof(path), name, prop) == 0) {
    goto found;
    }
    }
    ......
    }
    

    继续取得ro.product.board的值比如msmnile

    取得ro.board.platform的值比如msm8996(TARGET_BOARD_PLATFORM)

    取得ro.arch的值比如arm64(TARGET_ARCH)

    1. /hardware/libhardware/hardware.c
    int hw_get_module_by_class(const char *class_id, const char *inst,
    const struct hw_module_t **module) {
    ......
    /* Nothing found, try the default */
    if (hw_module_exists(path, sizeof(path), name, "default") == 0) {
    goto found;
    }
    ......
    }
    

    按照default查找,接下来看看是怎么判断该module是否存在的呢:

    /** Base path of the hal modules */ 
    #if defined(__LP64__) 
    #define HAL_LIBRARY_PATH1 "/system/lib64/hw" 
    #define HAL_LIBRARY_PATH2 "/vendor/lib64/hw" 
    #define HAL_LIBRARY_PATH3 "/odm/lib64/hw" 
    #else 
    #define HAL_LIBRARY_PATH1 "/system/lib/hw" 
    #define HAL_LIBRARY_PATH2 "/vendor/lib/hw" 
    #define HAL_LIBRARY_PATH3 "/odm/lib/hw" 
    #endif
    
    /* 
    * Check if a HAL with given name and subname exists, if so return 0, otherwise 
    * otherwise return negative. On success path will contain the path to the HAL. 
    */ 
    static int hw_module_exists(char *path, size_t path_len, const char *name,
    const char *subname) {
    snprintf(path, path_len, "%s/%s.%s.so", HAL_LIBRARY_PATH3, name, subname);
    if (access(path, R_OK) == 0)
    return 0;
    snprintf(path, path_len, "%s/%s.%s.so", HAL_LIBRARY_PATH2, name, subname);
    if (access(path, R_OK) == 0)
    return 0; 160 161#ifndef __ANDROID_VNDK__
    snprintf(path, path_len, "%s/%s.%s.so", HAL_LIBRARY_PATH1, name, subname);
    if (access(path, R_OK) == 0)
    return 0;
    #endif
    return -ENOENT;
    }
    

    也就是在如上路径下寻找对应的

    radio.fm.msm8996.so/radio.fm.arm64.so/radio.fm.default.so......

    若找到库与之匹配则,跳转到found.

    found: 
    /* load the module, if this fails, we're doomed, and we should not try 
    * to load a different variant. */ 
    return load(class_id, path, module);
    
    ......
    static int load(const char *id,
    const char *path, 
    const struct hw_module_t **pHmi) {
    ......
    /* 
    * load the symbols resolving undefined symbols before 
    * dlopen returns. Since RTLD_GLOBAL is not or'd in with 
    * RTLD_NOW the external symbols will not be global 
    */ 
    if (try_system && 
    strncmp(path, HAL_LIBRARY_PATH1, strlen(HAL_LIBRARY_PATH1)) == 0) { 
    /* If the library is in system partition, no need to check 
    * sphal namespace. Open it with dlopen. 
    */ 
    handle = dlopen(path, RTLD_NOW); 
    } else { 
    handle = android_load_sphal_library(path, RTLD_NOW); 
    }
    ......
    }
    

    通过dlopen拿到句柄,这里要注意路径,若非system下的库则会通过android_load_sphal_library来加载,可能会存在加载失败的问题,后续会单独开出章节叙述该问题。原生的库命名为radio.fm.default.so
    拿到句柄以后,我们从hardware.c回到BroadcastRadio.cpp

    /hardware/interfaces/broadcastradio/1.0/default/BroadcastRadio.cpp

    struct radio_hw_device *mHwDevice;
    void BroadcastRadio::onFirstRef()
    {
    const hw_module_t *mod;
    mHwDevice = NULL;
    rc = radio_hw_device_open(mod, &mHwDevice);
    }
    

    这里mHwDevice的声明是在BroadcastRadio.h中,结构体的声明在radio.h

    /hardware/libhardware/include/hardware/radio.h

    #define RADIO_HARDWARE_DEVICE "radio_hw_device"
    struct radio_hw_device {
    struct hw_device_t common;
    int (*get_properties)(const struct radio_hw_device *dev, 
    radio_hal_properties_t *properties);
    ......
    }
    
    static inline int radio_hw_device_open(const struct hw_module_t* module, 
    struct radio_hw_device** device)
    { 
    return module->methods->open(module, RADIO_HARDWARE_DEVICE, 
    TO_HW_DEVICE_T_OPEN(device));
    }
    

    关于 TO_HW_DEVICE_T_OPEN的定义参考

    /hardware/libhardware/include/hardware/hardware.h

    这里其实纠结了很久是怎样连接到库的函数的,但是如果看到so中的实现,则会比较容易理解。

    原生代码中Radio HAL的默认实现在:

    /hardware/libhardware/modules/radio/

    HAL moduel的实现,一般都在该路径

    /hardware/libhardware/modules/

    /hardware/libhardware/modules/radio/radio_hw.c

    static struct hw_module_methods_t hal_module_methods = {
    .open = rdev_open,
    };
    struct radio_module HAL_MODULE_INFO_SYM = {
    .common = {
    .tag = HARDWARE_MODULE_TAG,
    .module_api_version = RADIO_MODULE_API_VERSION_1_0,
    .hal_api_version = HARDWARE_HAL_API_VERSION,
    .id = RADIO_HARDWARE_MODULE_ID,
    .name = "Stub radio HAL",
    .author = "The Android Open Source Project",
    .methods = &hal_module_methods,
    },
    };
    

    /hardware/libhardware/include/hardware/radio.h

    struct radio_module {
    struct hw_module_t common;
    }
    

    可以看到common是hw_module_t类型,module是hw_module_t的类型指针

    所以module->methods->open也就是

    /hardware/libhardware/modules/radio/radio_hw.c

    下的rdev_open.

    /hardware/libhardware/include/hardware/radio.h 就是service和module之间沟通的头文件。

    而rdev_open中:

    /hardware/libhardware/modules/radio/radio_hw.c

    struct stub_radio_device {
    struct radio_hw_device device; 
    struct stub_radio_tuner *tuner;
    pthread_mutex_t lock;
    }
    tatic int rdev_open(const hw_module_t* module, const char* name, 
    hw_device_t** device) 
    {
    struct stub_radio_device *rdev;
    ......
    rdev->device.common.tag = HARDWARE_DEVICE_TAG; 
    rdev->device.common.version = RADIO_DEVICE_API_VERSION_1_0; 
    rdev->device.common.module = (struct hw_module_t *) module; 
    rdev->device.common.close = rdev_close; 
    rdev->device.get_properties = rdev_get_properties; 
    rdev->device.open_tuner = rdev_open_tuner; 
    rdev->device.close_tuner = rdev_close_tuner; 
    pthread_mutex_init(&rdev->lock, (const pthread_mutexattr_t *) NULL); 
    *device = &rdev->device.common; 
    return 0;
    }
    

    这里将各个方法复制给了在radio_hw_device中声明的方法指针,并通过device将赋过值的对象传回。

    /hardware/libhardware/include/hardware/radio.h

    struct radio_hw_device {
    struct hw_device_t common;
    int (*get_properties)(const struct radio_hw_device *dev, radio_hal_properties_t *properties);
    ......
    }
    

    至此,HIDL 的service已经拿到module的相关句柄和对象,可以进行调用了,这里HAL_MODULE_INFO_SYM的方式应该是涵盖了很多HIDL到HAL的沟通。

    相关文章

      网友评论

        本文标题:Radio Bringup(App-Framework-Hal)

        本文链接:https://www.haomeiwen.com/subject/pvyjzctx.html