简介
Radio 的逻辑其实很简单,但是bringup的过程涉及到了从app到hal的整个框架交互,是一个很好的入手HAL的机会,这里以bringup的角度,做了一次简单的代码走读,后续会陆续更新,Radio 的工作流程,常用术语,HIDL机制等。
结构图
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> ®istered) {
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,是从三个条件来匹配相关的库是否存在的。
- /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
- /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)
- /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的沟通。