1. 程式人生 > >從CarAudioManager呼叫流程開始學習Vendor Interface(Client部分)

從CarAudioManager呼叫流程開始學習Vendor Interface(Client部分)

1.需求

解決系統升級困難的問題(模組化)

a.以前用.h標頭檔案的形式,各自Vendor有各自的so去實現硬體功能。

現在改成.hal檔案的形式(Binder化),類似.aidl.

Thegoal of HIDL is that the framework can be replaced without having torebuild HALs. HALs will be built by vendors or SOC makers and put ina /vendor partitionon the device, enabling the framework, in its own partition, to bereplaced with an OTA without recompiling the HALs.

b.以前的輸出打包進system裡的一個個so

現在整個Vendor的東西應該是單獨的一個啥?是一個vendor.img(裡頭有so),那麼系統升級的時候,system.img就可以單拿出來。不用找各個硬體供應商,就可以用升級過的system.img去做替換升級

如圖所示(圖畫的比較醜,抱歉)
這裡寫圖片描述

這裡寫圖片描述

屁話少說,放碼過來。

「Talk is cheap. Show me the code」

RadioDemo中

CarAudioManger的初始化

//從CarService中取出Audio的
mCarAudioManager = (CarAudioManager) mCarApi.getCarManager(android.car.Car.AUDIO_SERVICE);

請求AudioFocus

mCarAudioManager.requestAudioFocus(this, mRadioAudioAttributes,AudioManager.AUDIOFOCUS_GAIN, 0);
//[email protected]
mAudioManager.requestAudioFocus

好吧,和普通手機的一樣,本集終!

看看最常用的setStreamVolume吧

mCarAudioManager.setStreamVolume(mStreamType, progress, AudioManager.FLAG_PLAY_SOUND);
//[email protected]
//檢查Car.PERMISSION_CAR_CONTROL_AUDIO_VOLUME許可權 enforceAudioVolumePermission(); mVolumeService.setStreamVolume(streamType, index, flags); //[email protected] getController().setStreamVolume(streamType, index, flags); //工廠 mCarVolumeController = CarVolumeControllerFactory.createCarVolumeController(mContext,mAudioService, mAudioHal, mInputService);

CarVolumeController是CarVolumeService的內部類

CarVolumeControllerFactory內部又定義了兩個CarVolumeController的子類:

SimpleCarVolumeController

//software mixer level

//還是最普通的,本分支劇終

mAudioManager.setStreamVolume(stream, index, flags);

CarExternalVolumeController

//直接跟audio模組,audio hal互動?

[email protected]@CarVolumeControllerFactory

setStreamVolumeInternalLocked(carContext, index, flags);

carContext的解釋:

Currently car context and android logical stream are not

one-to-one mapping. In this API, Android side asks us to change a logical stream volume. If the current car audio context maps to this logical stream, then we change the volume for the current car audio context. Otherwise, we change the volume for the primary mapped car audio context.

山寨翻譯:

當前的car context和安卓邏輯流不是一對一的。在這個API中,安卓側請求我們改變一個邏輯流音量。如果當前car audio context匹配這個邏輯流,我們為當前的car audio context改變音量。否則,我們為主匹配的car audio context音量。WTF,我也沒懂。

[email protected]

//Convert an car context to the car stream.
int carStream = carContextToCarStream(carContext);
// For single channel, only adjust the volume when the audio context is the current one.
    mHandler.sendMessage(mHandler.obtainMessage(MSG_UPDATE_HAL, carStream, index));
    ...
//MSG_UPDATE_HAL
    case MSG_UPDATE_HAL:
        ...
        mHal.setStreamVolume(stream, volume);

且看看mHal:

private final AudioHalService mHal;

public class AudioHalService extends HalServiceBase

HalServiceBase是什麼東西?

[email protected]

int[] payload = {streamType, index, 0};
mVehicleHal.set(VehicleProperty.AUDIO_VOLUME).to(payload);

mVehicleHal是什麼?

private final VehicleHal mVehicleHal;
public class VehicleHal extends IVehicleCallback.Stub

Abstraction for vehicle HAL. This class handles interface with native HAL and do basic parsing

of received data (type check). Then each event is sent to corresponding {@link HalServiceBase}

implementation. It is responsibility of {@link HalServiceBase} to convert data to corresponding

CarService for CarManager API.

山寨翻譯:

車機HAL的抽象。這個類處理和native HAL相關的interface,做一些收到資料的基本處理(型別檢查)。然後每一個event被送到對應的實現(HalServiceBase).它有責任為CarManager API轉換資料到對應的CarService.

mVehicleHal.set(VehicleProperty.AUDIO_VOLUME).to(payload);

set操作僅僅是說要修改的值,to操作才是真正傳值!

packages/services/Car/service/src/com/android/car/hal/VehicleHal.java

void set(VehiclePropValue propValue) throws PropertyTimeoutException {
         mHalClient.setValue(propValue);
     }

     @CheckResult
     VehiclePropValueSetter set(int propId) {
         return new VehiclePropValueSetter(mHalClient, propId, NO_AREA);
     }

     @CheckResult
     VehiclePropValueSetter set(int propId, int areaId) {
         return new VehiclePropValueSetter(mHalClient, propId, areaId);
     }

呼叫to函式

void to(int[] values) throws PropertyTimeoutException {
             for (int value : values) {
                 mPropValue.value.int32Values.add(value);
             }
             submit();
}

void submit() throws PropertyTimeoutException {
...
HalClient client =  mClient.get();
client.setValue(mPropValue);
...
}

[email protected]

import android.hardware.automotive.vehicle.V2_0.IVehicle;
IVehicle mVehicle;
    ...
mVehicle.set(propValue);
    ...

這個地方要插一句

hardware/interfaces/automotive/vehicle/2.0/types.hal中定義的

VehicleProperty.這個基本機構,基本包括了音量,空調溫度,轉向等等一些車相關的屬性。

也即

車機相關的東西控制,基本可以通過mVehicle.set(propValue)這種方式去控制

當然了,這裡除了mVehicle.set,也有public VehiclePropValue get(int propertyId)這樣的方法

IVehicle.java:

/out/target/common/gen/JAVA_LIBRARIES/android.hardware.automotive.vehicle-V2.0-java_intermediates/android/hardware/automotive/vehicle/V2_0

我們再往前追溯下:

mVehicle最終來自:

services/Car/service/src/com/android/car/CarService.java:

private static IVehicle getVehicle(@Nullable String interfaceName) {
...
    vehicle = android.hardware.automotive.vehicle.V2_0.IVehicle
                         .getService();
...
}

即自動生成的IVehicle.java中的:

//沒有引數的,預設getService
public static IVehicle getService() throws android.os.RemoteException {
          return IVehicle.asInterface(android.os.HwBinder.getService("[email protected]::IVehicle","default"));
}

這個地方不能錯過的一點是:

asInterface的引數:

android.os.HwBinder.getService(“[email protected]::IVehicle”,”default”)

//一個新加入的類

//[email protected]

public static native final IHwBinder getService(
            String iface,
            String serviceName)
        throws RemoteException, NoSuchElementException;

竟然是native方法!

[email protected]_os_HwBinder.cpp

//獲取hardware那邊的ServiceManager
//Android O中,每個硬體模組都會啟動一個Service程序
auto manager = hardware::defaultServiceManager();
//在這裡就是"[email protected]::IVehicle"
const char *ifaceNameCStr = env->GetStringUTFChars(ifaceNameObj, NULL);
std::string ifaceName(ifaceNameCStr);
...
//C++層的[email protected],配有自己的rc,
//這個地方取到的十有八九都是C++層的[email protected]服務了
//目前先不去詳細論證了
Return<sp<hidl::base::V1_0::IBase>> ret = manager->get(ifaceNameHStr, serviceNameHStr);
sp<hardware::IBinder> service = hardware::toBinder<
            hidl::base::V1_0::IBase, hidl::base::V1_0::BpHwBase>(ret);
return JHwRemoteBinder::NewObject(env, service);

即相當於

binder = (各種轉換)JHwRemoteBinder::NewObject(env, 名字為“[email protected]::IVehicle”的服務);

JHwRemoteBinder::NewObject

// static
jobject JHwRemoteBinder::NewObject(
        JNIEnv *env, const sp<hardware::IBinder> &binder) {
        //CLASS_PATH = android/os/HwRemoteBinder
    ScopedLocalRef<jclass> clazz(env, FindClassOrDie(env, CLASS_PATH));

    // XXX Have to look up the constructor here because otherwise that static
    // class initializer isn't called and gProxyOffsets.constructID is undefined :(

    jmethodID constructID = GetMethodIDOrDie(env, clazz.get(), "<init>", "()V");
    //構造了一個java的HwRemoteBinder物件
    jobject obj = env->NewObject(clazz.get(), constructID);
    //GetNativeContext獲取
    //上一步生成的HwRemoteBinder物件的mNativeContext變數
    //應該是之前設定過
    //然後呼叫setBinder
    //之後呼叫getBinder取到的就是這個IVehicle服務了
    JHwRemoteBinder::GetNativeContext(env, obj)->setBinder(binder);

    return obj;
}

到這一步結束,隱隱yueyue地,java和C++關聯起來了!

上回說到,IVehicle.java中

getService中,呼叫了

IVehicle.asInterface(上邊的native層的Vehicle服務相關的binder)

static IVehicle asInterface(android.os.IHwBinder binder) {
...
IVehicle proxy = new IVehicle.Proxy(binder);
...
return proxy;
...
}

建構函式[email protected]

public Proxy(android.os.IHwBinder remote) {
            mRemote = java.util.Objects.requireNonNull(remote);
        }

那前面呼叫的set函式應該是這個:

public static final class Proxy implements IVehicle{
...
@Override
         public int set(VehiclePropValue propValue)
                 throws android.os.RemoteException {
             android.os.HwParcel _hidl_request = new android.os.HwParcel();
             _hidl_request.writeInterfaceToken(IVehicle.kInterfaceName);
             propValue.writeToParcel(_hidl_request);
             android.os.HwParcel _hidl_reply = new android.os.HwParcel();
             try {
                 mRemote.transact(4 /* set */, _hidl_request, _hidl_reply, 0 /* flags */);
                 _hidl_reply.verifySuccess();
                 _hidl_request.releaseTemporaryStorage();
                  int _hidl_out_status = _hidl_reply.readInt32();
                 return _hidl_out_status;
             } finally {
                 _hidl_reply.release();
             }
         }
...
}

注意:該檔案中有onTransact的一端。應該是提供給另外一種呼叫方式的,我之前就走歪了!