Radio Bringup(App-Framework-Hal)

简介

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的沟通。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 205,236评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,867评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,715评论 0 340
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,899评论 1 278
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,895评论 5 368
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,733评论 1 283
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,085评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,722评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,025评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,696评论 2 323
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,816评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,447评论 4 322
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,057评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,009评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,254评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,204评论 2 352
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,561评论 2 343

推荐阅读更多精彩内容

  • 从相识到相恋,芊羽和男友Y历经10年光阴,可谓是一对青梅竹马的恋人,曾经甜蜜得让人羡慕妒忌得有些眼红,但最终还是在...
    傲骄女性美丽说阅读 458评论 0 1
  • 所有子元素浮动,父类加上class='clearfix' 开发者工具 computed 网页计算出样式的像素大小 ...
    shirley_ee84阅读 171评论 0 0