Radio Bringup(App-Framework-Hal)

簡介

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

結(jié)構(gòu)圖

image.png

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> &registered) {
        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,是從三個條件來匹配相關的庫是否存在的。

  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;
}
}
......
}

繼續(xù)取得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......

若找到庫與之匹配則,跳轉(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的溝通。

?著作權歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關閱讀更多精彩內(nèi)容

  • 從相識到相戀,芊羽和男友Y歷經(jīng)10年光陰,可謂是一對青梅竹馬的戀人,曾經(jīng)甜蜜得讓人羨慕妒忌得有些眼紅,但最終還是在...
    傲驕女性美麗說閱讀 570評論 0 1
  • 所有子元素浮動,父類加上class='clearfix' 開發(fā)者工具 computed 網(wǎng)頁計算出樣式的像素大小 ...
    shirley_ee84閱讀 237評論 0 0

友情鏈接更多精彩內(nèi)容