簡介
Radio 的邏輯其實很簡單,但是bringup的過程涉及到了從app到hal的整個框架交互,是一個很好的入手HAL的機會,這里以bringup的角度,做了一次簡單的代碼走讀,后續(xù)會陸續(xù)更新,Radio 的工作流程,常用術語,HIDL機制等。
結(jié)構(gòu)圖

Bringup流程
對外接口在framewrok的RadioManager和TunerAdapter實現(xiàn)。
LINUX/android/frameworks/base/core/java/android/hardware/radio/RadioManager.java
LINUX/android/frameworks/base/core/java/android/hardware/radio/TunerAdapter.java
并依賴回調(diào)異步返回請求的數(shù)據(jù)。
LINUX/android/frameworks/base/core/java/android/hardware/radio/TunerCallbackAdapter.java
RadioManager通過AIDL調(diào)用Radio跑在framework的系統(tǒng)服務。
關于AIDL的實現(xiàn)方法和實現(xiàn)原理后續(xù)贅述,這里整理下調(diào)查之后的系統(tǒng)服務的實現(xiàn)過程。
RadioManager中找到service是通過:
mService = IRadioService.Stub.asInterface(
ServiceManager.getServiceOrThrow(Context.RADIO_SERVICE));
這里的ServiceManager的實現(xiàn):
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這個關鍵字入手。
從全局的搜索結(jié)果來看,在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到。
按理說調(diào)查到這里,整個接口都是完整的,我們的服務可以取到并正常運行了,但是事實證明RadioManager中一直取不到Radio的服務,編譯未報錯,
運行報錯。
所以說該服務并沒有起來,繼而開始調(diào)查系統(tǒng)服務的啟動過程。
既然是一個服務,要啟動,無非是startService或者是bind,這里的系統(tǒng)服務,調(diào)用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()的調(diào)用在SystemServer的run()方法中:
public static void main(String[] args) {
new SystemServer().run();
}
private void run() {
......
startOtherServices()
......
}
而具體SystemServer的啟動時機,我沒有找到,網(wǎng)上搜索來看,是由一個rc文件拉起來的。
[https://blog.csdn.net/user11223344abc/article/details/80625185]
Android系統(tǒng)是基于Linux內(nèi)核的,而在Linux系統(tǒng)中,所有的進程都是init進程的子孫進程,也就是說,
所有的進程都是直接或者間接地由init進程fork出來的。Zygote進程也不例外,它是在系統(tǒng)啟動的過程,由init進程創(chuàng)建的。
在系統(tǒng)啟動腳本system/core/rootdir/init.rc文件中?!系踚nit.rc
系統(tǒng)啟動時init進程會創(chuàng)建Zygote進程(準確的說是通過app_main.cpp啟動的Zygote進程),
Zygote進程負責后續(xù)Android應用程序框架層的其它進程的創(chuàng)建和啟動工作?!猘ndroid大boss,孵化進程
Zygote進程會首先創(chuàng)建一個SystemServer進程,SystemServer進程負責啟動系統(tǒng)的關鍵服務,如AMS,PMS等?!猘ndroid大boss首席小弟
后續(xù)會在開機啟動優(yōu)化的文檔中詳細跟蹤流程。
也就是說開機啟動SystemServer進程,從而把framewrok的系統(tǒng)服務拉起來,這里既然沒起來,必然是判斷沒有走過,那這個判斷又是個什么東西。。。
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";
從定義來看,是配置的系統(tǒng)是否支持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配置文件中進行的配置。
后續(xù)調(diào)查發(fā)現(xiàn),一般是從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的系統(tǒng)服務調(diào)查結(jié)束,開始向HIDL和HAL深入。
調(diào)用到達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部分的向下調(diào)用通過HIDL,關于HIDL的實現(xiàn)方法和實現(xiàn)原理后續(xù)贅述:
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,這里看到入?yún)⒂袃蓚€,第一個是HAL服務注冊的名字,第二個是返回該注冊對應的服務列表,我們一步一步來看是怎么通過名字來獲得服務的,而這個名字又是在哪里傳進來的。
系統(tǒng)中關于HIDL的代碼一般會放在:
hardware/interfaces 下面,
radio hidl的接口代碼位置--
hardware/interfaces/broadcastradio/
Android比較新的版本都在推Android.bp,后續(xù)會詳細介紹Android.bp和HAL的相關結(jié)構(gòu)情況,這里繼續(xù)針對bringup的流程來介紹。
當時radio還停留在1的版本迭代,在Android P升級到2的版本并開始從HAL的service直接和framework層交互。
除掉test的相關代碼后,代碼結(jié)構(gòu)如下:
├── 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++調(diào)用者使用的h和cpp文件以及針對java調(diào)用者使用的java文件,從jni的代碼中調(diào)用的是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的接口后就可以發(fā)現(xiàn)同樣的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");
而第二個入?yún)egistered,傳的是一個引用,用來存儲我們要用的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.
則給第二個入?yún)①x值的service是通過遍歷這個interfaceChain,并檢查對應的service是否已經(jīng)注冊,然后返回的。
接下來,我們回到radio的service。
在1.1版本存在的情況下,這里應該會拿到1.1的service.通過castFrom來獲取各個類并調(diào)用接口。
LINUX/android/frameworks/base/services/core/jni/BroadcastRadio/BroadcastRadioService.cpp
詳細的接口調(diào)用我們這里不再贅述,還是回到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的對象轉(zhuǎn)換為jni對象都是在convert中做的,詳細的轉(zhuǎn)化過程可以自行在
frameworks/base/services/core/jni/BroadcastRadio/convert.cpp
中查看。
流程走到這里,如果debug都順利應該就說明與HAL 的服務交互正常,然而,事情怎會如此簡單~~
這里并沒有拿到HAL 的service,從adb shell ps -A 的情況來看,radio hal的service也確實沒有被拉起來。
回到HAL 的HIDL這里,我們來繼續(xù)確認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();
......
}
而該服務確實是被編譯進可執(zhí)行文件的:
/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文件和可執(zhí)行文件是否存在。
從上面的Android.bp可以看到是編譯在vendor下
adb shell進入設備,在vendor/etc/init下是存放各個模塊的rc文件,從而可以做到在開機啟動的時候執(zhí)行該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的權限添加,可能不全,需要根據(jù)具體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的服務被正常拉起。
此時如果已經(jīng)安裝了radio的apk,會發(fā)現(xiàn)已經(jīng)可以成功拉起,并且出現(xiàn)可用電臺,但是出現(xiàn)的電臺并非是我們想要的實時電臺,而大多是虛擬電臺。從原生1.1的代碼來看,HIDL的default實現(xiàn)中添加了很多模擬數(shù)據(jù)。
│ │ ├── VirtualProgram.cpp
│ │ ├── VirtualProgram.h
│ │ ├── VirtualRadio.cpp
│ │ └── VirtualRadio.h
所以需要參考1.0的實現(xiàn)來完善1.1同底層的交互,當然也可以直接采用1.1的版本。不管是哪個版本,我們都來看下HIDL在這里是怎樣實現(xiàn)與HAL層的通信的。
從之前jni的邏輯來看,初步進行接口調(diào)用的是BroadcastRadio,這里我們來看看相關內(nèi)容應該是在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結(jié)構(gòu)體的聲明在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傳入的入?yún)⒖梢灾纍ame的值為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做了一個循環(huán),但是因為我們只針對AM_FM做了module,所以其他的class值雖然也會傳入,但是都會load失敗,這里只看fm。
繼續(xù)回到剛才的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;
}
}
......
}
繼續(xù)取得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......
若找到庫與之匹配則,跳轉(zhuǎn)到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來加載,可能會存在加載失敗的問題,后續(xù)會單獨開出章節(jié)敘述該問題。原生的庫命名為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中,結(jié)構(gòu)體的聲明在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
這里其實糾結(jié)了很久是怎樣連接到庫的函數(shù)的,但是如果看到so中的實現(xiàn),則會比較容易理解。
原生代碼中Radio HAL的默認實現(xiàn)在:
/hardware/libhardware/modules/radio/
HAL moduel的實現(xiàn),一般都在該路徑
/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已經(jīng)拿到module的相關句柄和對象,可以進行調(diào)用了,這里HAL_MODULE_INFO_SYM的方式應該是涵蓋了很多HIDL到HAL的溝通。