Android O 前期預研之三:Android Vehicle HAL

Android Automotive

Android Automotive 是Android Oreo中的一個新的特色功能,從AOSP的代碼上來看,Android O中已經包含有了從Application到Framework 到HAL的整體框架,這一章節,我們簡單的過以下Android Vehicle 的框架,以及重點看下 Vehicle HAL的東西。總體結構大約是以下這個樣子: 
這裏寫圖片描述

上圖的結構應該是Android Oreo當中比較通用的框架結構了,從Application 到Framework到HAL,跟之前的Android版本相比,之前Framework要不通過binder聯繫上一個Daemon,這個Daemon再去load 相關的HAL,要不就是這些Framework的service直接通過JNI去load 這些個HAL 庫。而現在的Android Oreo則是Framework 與HAL之間直接採用HIDL來做聯絡溝通了。接下來我們從下往上的來把Android Vehicle的框架捋一下吧。首先來分析下Vehicle HAL,通過這個來複習並且實踐下之前研究學習過的Android HIDL.

Android Vehicle HAL

這裏寫圖片描述

types.hal 定義的是一些數據結構,IVehicle.hal定義的是從Framework往HAL調用的接口,而IVehicleCallback.hal則是HAL往Framework 上報回調的接口。看起來還是挺清晰的吧。 
而IVehicle.hal的接口也不是很多,

package [email protected];

import IVehicleCallback;

interface IVehicle {
  /**
   * Returns a list of all property configurations supported by this vehicle
   * HAL.
   */
  getAllPropConfigs() generates (vec<VehiclePropConfig> propConfigs);

  /**
   * Returns a list of property configurations for given properties.
   *
   * If requested VehicleProperty wasn't found it must return
   * StatusCode::INVALID_ARG, otherwise a list of vehicle property
   * configurations with StatusCode::OK
   */
  getPropConfigs(vec<int32_t> props)
          generates (StatusCode status, vec<VehiclePropConfig> propConfigs);

  /**
   * Get a vehicle property value.
   *
   * For VehiclePropertyChangeMode::STATIC properties, this method must always
   * return the same value always.
   * For VehiclePropertyChangeMode::ON_CHANGE properties, it must return the
   * latest available value.
   *
   * Some properties like AUDIO_VOLUME requires to pass additional data in
   * GET request in VehiclePropValue object.
   *
   * If there is no data available yet, which can happen during initial stage,
   * this call must return immediately with an error code of
   * StatusCode::TRY_AGAIN.
   */
  get(VehiclePropValue requestedPropValue)
          generates (StatusCode status, VehiclePropValue propValue);

  /**
   * Set a vehicle property value.
   *
   * Timestamp of data must be ignored for set operation.
   *
   * Setting some properties require having initial state available. If initial
   * data is not available yet this call must return StatusCode::TRY_AGAIN.
   * For a property with separate power control this call must return
   * StatusCode::NOT_AVAILABLE error if property is not powered on.
   */
  set(VehiclePropValue propValue) generates (StatusCode status);

  /**
   * Subscribes to property events.
   *
   * Clients must be able to subscribe to multiple properties at a time
   * depending on data provided in options argument.
   *
   * @param listener This client must be called on appropriate event.
   * @param options List of options to subscribe. SubscribeOption contains
   *                information such as property Id, area Id, sample rate, etc.
   */
  subscribe(IVehicleCallback callback, vec<SubscribeOptions> options)
          generates (StatusCode status);

  /**
   * Unsubscribes from property events.
   *
   * If this client wasn't subscribed to the given property, this method
   * must return StatusCode::INVALID_ARG.
   */
  unsubscribe(IVehicleCallback callback, int32_t propId)
          generates (StatusCode status);

  /**
   * Print out debugging state for the vehicle hal.
   *
   * The text must be in ASCII encoding only.
   *
   * Performance requirements:
   *
   * The HAL must return from this call in less than 10ms. This call must avoid
   * deadlocks, as it may be called at any point of operation. Any synchronization
   * primitives used (such as mutex locks or semaphores) must be acquired
   * with a timeout.
   *
   */
  debugDump() generates (string s);
};

而IVehicle.hal則就更少了:

package [email protected];

interface IVehicleCallback {

    /**
     * Event callback happens whenever a variable that the API user has
     * subscribed to needs to be reported. This may be based purely on
     * threshold and frequency (a regular subscription, see subscribe call's
     * arguments) or when the IVehicle#set method was called and the actual
     * change needs to be reported.
     *
     * These callbacks are chunked.
     *
     * @param values that has been updated.
     */
    oneway onPropertyEvent(vec<VehiclePropValue> propValues);

    /**
     * This method gets called if the client was subscribed to a property using
     * SubscribeFlags::SET_CALL flag and IVehicle#set(...) method was called.
     *
     * These events must be delivered to subscriber immediately without any
     * batching.
     *
     * @param value Value that was set by a client.
     */
    oneway onPropertySet(VehiclePropValue propValue);

    /**
     * Set property value is usually asynchronous operation. Thus even if
     * client received StatusCode::OK from the IVehicle::set(...) this
     * doesn't guarantee that the value was successfully propagated to the
     * vehicle network. If such rare event occurs this method must be called.
     *
     * @param errorCode - any value from StatusCode enum.
     * @param property - a property where error has happened.
     * @param areaId - bitmask that specifies in which areas the problem has
     *                 occurred, must be 0 for global properties
     */
    oneway onPropertySetError(StatusCode errorCode,
                              int32_t propId,
                              int32_t areaId);
};

比較好奇的是這麼些接口就能實現Android 車機的這麼些功能?先還是這麼看看吧,後續仔細研究研究。

Android Vehicle HAL 的編譯

按照我們之前的研究來看看這麼些Android Vehicle HAL被怎麼編譯,編譯成什麼東西。先看.hal文件編譯生成的頭文件: 
這裏寫圖片描述

.hal 文件生成的CPP文件: 
這裏寫圖片描述

.h 文件我們之前也都做過分析,Bp/Bn/Bs 代表啥意思相信都沒忘記吧,在這裏就不多贅述了,而VehicleAll.cpp/VehicleCallbackAll.cpp 裏其實就是那些生成的.h文件中所定義的C++ Class 文件的實現,這些相關實現都是寫在這些.cpp文件當中。

這些.cpp文件/.h文件最終會生成一個名叫[email protected]的庫,詳情請參考hardware/interfaces/automotive/vehicle/2.0/Android.bp。

另外參考hardware/interfaces/automotive/vehicle/2.0/default/Android.mk, 
hardware/interfaces/automotive/vehicle/2.0/default/impl/目錄下會被編譯出名叫[email protected]的靜態庫,而hardware/interfaces/automotive/vehicle/2.0/default/common/目錄會被編譯出一個名叫[email protected]的靜態庫,而這兩個靜態庫都會去鏈接上面生成的[email protected]動態庫。

而最終會被編譯成一個可執行程序:

include $(CLEAR_VARS)
LOCAL_MODULE := $(vhal_v2_0)-service
LOCAL_INIT_RC := $(vhal_v2_0)-service.rc
LOCAL_PROPRIETARY_MODULE := true
LOCAL_MODULE_RELATIVE_PATH := hw

LOCAL_SRC_FILES := \
    VehicleService.cpp

LOCAL_SHARED_LIBRARIES := \
    libbase \
    libhidlbase \
    libhidltransport \
    liblog \
    libprotobuf-cpp-lite \
    libutils \
    $(vhal_v2_0) \

LOCAL_STATIC_LIBRARIES := \
    $(vhal_v2_0)-manager-lib \
    $(vhal_v2_0)-default-impl-lib \
    $(vhal_v2_0)-libproto-native \

LOCAL_CFLAGS += -Wall -Wextra -Werror

include $(BUILD_EXECUTABLE)

而這個可執行程序會被Init 系統在開機的時候啓動,成爲一個Daemon: 
service vehicle-hal-2.0 /vendor/bin/hw/[email protected] 
class hal 
user vehicle_network 
group system inet

Android Vehicle HAL 的使用

東西都編譯出來後,我們看看Vehicle HAL怎麼來使用。

1) Android Vehicle HAL Service 端使用: 
我們來看下hardware/interfaces/automotive/vehicle/2.0/default/VehicleService.cpp文件:

int main(int /* argc */, char* /* argv */ []) {
    auto store = std::make_unique<VehiclePropertyStore>();
    auto hal = std::make_unique<impl::EmulatedVehicleHal>(store.get());
    auto emulator = std::make_unique<impl::VehicleEmulator>(hal.get());
    auto service = std::make_unique<VehicleHalManager>(hal.get());

    configureRpcThreadpool(4, true /* callerWillJoin */);

    ALOGI("Registering as service...");
    service->registerAsService();

    ALOGI("Ready");
    joinRpcThreadpool();
}

說實話,剛看到這段代碼的我是一臉懵逼的,auto是個什麼鬼, std::make_unique是個什麼鬼,這幾年弄的都是JAVA/Android,像這些C++的新規範,新東西越來越層出不窮了,真的是一天不學習就得落伍了哈。

幸虧這個也很簡單, std::make_unique就先等同與new吧,auto的意思就是變量類型在聲明的時候先不確定,等變量被定義的時候根據實際情況來確定變量類型。

其中最關鍵的是這兩句:

auto service = std::make_unique<VehicleHalManager>(hal.get());
ALOGI("Registering as service...");
service->registerAsService();

我們看下 VehicleHalManager的定義:

  class VehicleHalManager : public IVehicle 

VehicleHalManager類是由 IVehicle類派生出來,而IVehicle則是從我們上面介紹的.hal文件裏編譯出來的。

我們繼續來看下下面這句:

   service->registerAsService();

我們從.hal文件編譯出來的.cpp文件來看下,這個中間生成的cpp文件所在目錄爲:out/soong/.intermediates/hardware/interfaces/automotive/vehicle/2.0/[email protected]_genc++/gen/android/hardware/automotive/vehicle/2.0/VehicleAll.cpp

該函數的具體實現爲:

::android::status_t IVehicle::registerAsService(const std::string &serviceName) {
    ::android::hardware::details::onRegistration("[email protected]", "IVehicle", serviceName);

    const ::android::sp<::android::hidl::manager::V1_0::IServiceManager> sm
            = ::android::hardware::defaultServiceManager();
    // 獲取到默認的service manager,非常類似於 Binder機制中。有時間的話可以深入研究下。

    if (sm == nullptr) {
        return ::android::INVALID_OPERATION;
    }
    ::android::hardware::Return<bool> ret = sm->add(serviceName.c_str(), this);
    //往service manager 中添加service, 跟Binder機制也是一樣一樣的。

    return ret.isOk() && ret ? ::android::OK : ::android::UNKNOWN_ERROR;
}

但是比較奇怪的是add 進去是this 指針,也就是應該是 IVehicle對象,也就是VehicleHalManager的對象。不應該是Bnxxxx的對象麼????我這裏有一臉懵逼。這是第一個懵點,第二個懵點則來的更加不可思議:

const char* IVehicle::descriptor("[email protected]::IVehicle");

__attribute__((constructor))static void static_constructor() {
    ::android::hardware::details::gBnConstructorMap.set(IVehicle::descriptor,
            [](void *iIntf) -> ::android::sp<::android::hardware::IBinder> {
                return new BnHwVehicle(static_cast<IVehicle *>(iIntf));
            });
    ::android::hardware::details::gBsConstructorMap.set(IVehicle::descriptor,
            [](void *iIntf) -> ::android::sp<::android::hidl::base::V1_0::IBase> {
                return new BsVehicle(static_cast<IVehicle *>(iIntf));
            });
};

我們來看看這段代碼,首先attribute((constructor))也是gcc 的一個機制,大致意思是在main函數被調用之前 static_constructor函數會被調用。


::android::hardware::details::gBnConstructorMap.set(IVehicle::descriptor, 
[](void *iIntf) -> ::android::sp<::android::hardware::IBinder> { 
return new BnHwVehicle(static_cast<IVehicle *>(iIntf)); 
}); 

這段代碼意思是指在 gBnConstructorMap中插入一個key/value,這個value 值是

[](void *iIntf) -> ::android::sp<::android::hardware::IBinder> {
                return new BnHwVehicle(static_cast<IVehicle *>(iIntf));
            }

這又是什麼鬼啊???搜了下應該是拉曼達表達式,但是懵點是 void *iIntf 這個是參數,這個參數是怎麼傳遞進來的?我都搜了半天都沒找到參數怎麼傳遞進來的。但是看了下BnHwVehicle這個類的定義,明顯是使用了 IVehicle類。

BnHwVehicle::BnHwVehicle(const ::android::sp<IVehicle> &_hidl_impl)
        : ::android::hidl::base::V1_0::BnHwBase(_hidl_impl, "[email protected]", "IVehicle") { 
            _hidl_mImpl = _hidl_impl;
            auto prio = ::android::hardware::details::gServicePrioMap.get(_hidl_impl, {SCHED_NORMAL, 0});
            mSchedPolicy = prio.sched_policy;
            mSchedPriority = prio.prio;
}


::android::status_t BnHwVehicle::onTransact(
        uint32_t _hidl_code,
        const ::android::hardware::Parcel &_hidl_data,
        ::android::hardware::Parcel *_hidl_reply,
        uint32_t _hidl_flags,
        TransactCallback _hidl_cb) {
    ::android::status_t _hidl_err = ::android::OK;

    switch (_hidl_code) {
        case 1 /* getAllPropConfigs */:
        {
            if (!_hidl_data.enforceInterface(IVehicle::descriptor)) {
                _hidl_err = ::android::BAD_TYPE;
                break;
            }
           …………………
           ………………...
            bool _hidl_callbackCalled = false;

            _hidl_mImpl->getAllPropConfigs([&](const auto &_hidl_out_propConfigs) {
            …………………….
            …………………...

            break;
        }

從BnHwVehicle這個類來看,它確實承擔的是HIDL Service的工作,從BnHwVehicle裏會來調用IVehicle類,IVehicle類會被繼承成爲真正的實現操作類。這個懵點2,到目前還沒有找到怎麼解決點。

到這裏,我們來簡單總結service端: 
1) 從Ixxxx類中會派生出新的類,這個類會作爲真正的操作類。 
2) 從Ixxxx類的派生類中調用 registerAsService 去把自己註冊進hw service manager中。 
3) 目前看起來,如果有client 發出請求的話,BnXXXXX會相應,BnXXXXX會調用自己的Ixxxx的實現函數來完成某一個操作請求。

1) Android Vehicle HAL Client 端使用: 
這個Client端存在Android Framework的Car Service當中 
在onCreate 函數當中:

“` 
@Override 
public void onCreate() { 
Log.i(CarLog.TAG_SERVICE, “Service onCreate”); 
mCanBusErrorNotifier = new CanBusErrorNotifier(this /* context */); 
mVehicle = getVehicle(null /* Any Vehicle HAL interface name */);

    if (mVehicle == null) {
        throw new IllegalStateException("Vehicle HAL service is not available.");
    }
    try {
        mVehicleInterfaceName = mVehicle.interfaceDescriptor();
    } catch (RemoteException e) {
        throw new IllegalStateException("Unable to get Vehicle HAL interface descriptor", e);
    }

    Log.i(CarLog.TAG_SERVICE, "Connected to " + mVehicleInterfaceName);

    mICarImpl = new ICarImpl(this, mVehicle, SystemInterface.getDefault(this),
            mCanBusErrorNotifier);
    mICarImpl.init();
    SystemProperties.set("boot.car_service_created", "1");

    linkToDeath(mVehicle, mVehicleDeathRecipient);

    super.onCreate();
}

mVehicle = getVehicle(null /* Any Vehicle HAL interface name */);

獲取到Vehicle HAL.

@Nullable
private static IVehicle getVehicle(@Nullable String interfaceName) {
    try {
        boolean anyVersion = interfaceName == null || interfaceName.isEmpty();
        IVehicle vehicle = null;
        if (ENABLE_VEHICLE_HAL_V2_1 && (anyVersion || IVHAL_21.equals(interfaceName))) {
            vehicle = android.hardware.automotive.vehicle.V2_1.IVehicle
                    .getService();
        }

        if (vehicle == null && (anyVersion || IVHAL_20.equals(interfaceName))) {
            vehicle = android.hardware.automotive.vehicle.V2_0.IVehicle
                    .getService();
        }
        return vehicle;
    } catch (RemoteException e) {
        Log.e(CarLog.TAG_SERVICE, "Failed to get IVehicle service", e);
    } catch (NoSuchElementException e) {
        Log.e(CarLog.TAG_SERVICE, "IVehicle service not registered yet");
    }
    return null;
}

理論上拿到IVehicle vehicle 對象後,就應該能夠來做對Vehicle HAL做相關調用了。這一部分就先到這裏完結吧。

發佈了35 篇原創文章 · 獲贊 28 · 訪問量 17萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章