[Android O] HAL3 之 Open Camera2 流程

作者:StoneDemo  來源:CSDN  原文:https://blog.csdn.net/qq_16775897/article/details/81537710 


 Camera HAL3 構建連路的過程,其總體框架可以通過下圖直觀地感受一下。 
紅色虛線是上行路線,黑色虛線則是下行路線。 

@å¾. æ»è§

 

總的來說,會分成三大部分來分析:

  1. 從 App 連接到 CameraService;
  2. 從 CameraService 連接到 HAL Service;
  3. 從 HAL Service 連接到 Camera HAL。

從 Application 連接到 CameraService,這涉及到 Android 架構中的三個層次:App 層,Framework 層,Runtime 層。 
其中,App 層直接調用 Framework 層所封裝的方法,而 Framework 層需要通過 Binder 遠程調用 Runtime 中 CameraService 的函數。

這一部分主要的函數調用邏輯如下圖所示。 
@å¾. App to CS å½æ°è°ç¨é»è¾

App


在 App 中,需要調用打開相機的接口,如下。

其中:

  • mCameraManager 是 CameraManager 類的實例。
  • currentCameraId 則是需要打開的相機設備號。
  • stateCallback 是 CameraDevice.StateCallback,是關於相機打開情況的相關回調。
  • backgroundHandler 則是 StateCallback 需要調用的 Handler。
mCameraManager.openCamera(currentCameraId, stateCallback, backgroundHandler);

Framework CameraManager


文件路徑:/frameworks/base/core/java/android/hardware/camera2/CameraManager.java

最初的入口就是 CameraManager 的 openCamera 方法。 
但通過代碼可以看到,它僅僅是調用了 openCameraForUid 方法。
 

@RequiresPermission(android.Manifest.permission.CAMERA)
public void openCamera(@NonNull String cameraId,
        @NonNull final CameraDevice.StateCallback callback, @Nullable Handler handler)
        throws CameraAccessException {

    openCameraForUid(cameraId, callback, handler, USE_CALLING_UID);
}

下面的代碼忽略掉了一些參數檢查相關操作,最終主要調用了 openCameraDeviceUserAsync 方法。

public void openCameraForUid(@NonNull String cameraId,
        @NonNull final CameraDevice.StateCallback callback, @Nullable Handler handler,
        int clientUid)
        throws CameraAccessException {
    /* Do something in*/
    ......
    /* Do something out*/
    openCameraDeviceUserAsync(cameraId, callback, handler, clientUid);
}

分析下面的代碼:

  • 第 11~17 行,首先實例化一個 CameraDeviceImpl。值得注意的是,構造時傳入了 CameraDevice.StateCallback 以及 Handler。
  • 第 19 行,獲取 CameraDeviceCallback 實例,這是提供給遠端連接到 CameraDeviceImpl 的接口。
  • 第 22~32 行,HAL3 中走的是這一部分邏輯,主要是從 CameraManagerGlobal 中獲取 CameraService 的本地接口,通過它遠端調用(採用 Binder 機制) connectDevice 方法連接到相機設備。注意返回的 cameraUser 實際上指向的是遠端 CameraDeviceClient 的本地接口。
  • 爲了縮短篇幅,省略了捕獲異常的相關代碼。
  • 第 56 行,將 CameraDeviceClient 設置到 CameraDeviceImpl 中進行管理。
private CameraDevice openCameraDeviceUserAsync(String cameraId,
        CameraDevice.StateCallback callback, Handler handler, final int uid)
        throws CameraAccessException {
    CameraCharacteristics characteristics = getCameraCharacteristics(cameraId);
    CameraDevice device = null;

    synchronized (mLock) {

        ICameraDeviceUser cameraUser = null;

        android.hardware.camera2.impl.CameraDeviceImpl deviceImpl =
                new android.hardware.camera2.impl.CameraDeviceImpl(
                    cameraId,
                    callback,
                    handler,
                    characteristics,
                    mContext.getApplicationInfo().targetSdkVersion);

        ICameraDeviceCallbacks callbacks = deviceImpl.getCallbacks();

       try {
            if (supportsCamera2ApiLocked(cameraId)) {
                // Use cameraservice's cameradeviceclient implementation for HAL3.2+ devices
                ICameraService cameraService = CameraManagerGlobal.get().getCameraService();
                if (cameraService == null) {
                    throw new ServiceSpecificException(
                        ICameraService.ERROR_DISCONNECTED,
                        "Camera service is currently unavailable");
                }
                cameraUser = cameraService.connectDevice(callbacks, cameraId,
                        mContext.getOpPackageName(), uid);
            } else {
                // Use legacy camera implementation for HAL1 devices
                int id;
                try {
                    id = Integer.parseInt(cameraId);
                } catch (NumberFormatException e) {
                    throw new IllegalArgumentException("Expected cameraId to be numeric, but it was: "
                            + cameraId);
                }

                Log.i(TAG, "Using legacy camera HAL.");
                cameraUser = CameraDeviceUserShim.connectBinderShim(callbacks, id);
            }
        } catch (ServiceSpecificException e) {
            /* Do something in */
            ......
            /* Do something out */
        }

        // TODO: factor out callback to be non-nested, then move setter to constructor
        // For now, calling setRemoteDevice will fire initial
        // onOpened/onUnconfigured callbacks.
        // This function call may post onDisconnected and throw CAMERA_DISCONNECTED if
        // cameraUser dies during setup.
        deviceImpl.setRemoteDevice(cameraUser);
        device = deviceImpl;
    }

    return device;
}

CameraDeviceImpl


文件路徑:/frameworks/base/core/java/android/hardware/camera2/Impl/CameraDeviceImpl.java

在繼續向下分析打開相機流程之前,先簡單看看調用到的 CameraDeviceImpl 中的方法。 
setRemoteDevice 方法主要是將獲取到的遠端設備保存起來:

  • 第 14 行,通過 ICameraDeviceUserWrapper 給遠端設備實例加上一層封裝。
  • 第 16~28 行,是使用 Binder 機制的一些基本設置。
  • 第 30、31 行,需要注意這個時間節點,此處觸發 onOpened 與 onUnconfigured 這兩個回調,每個回調都是通過 mDeviceHandler 啓用一個新線程來調用的。
/**
 * Set remote device, which triggers initial onOpened/onUnconfigured callbacks
 *
 * <p>This function may post onDisconnected and throw CAMERA_DISCONNECTED if remoteDevice dies
 * during setup.</p>
 *
 */
public void setRemoteDevice(ICameraDeviceUser remoteDevice) throws CameraAccessException {
    synchronized(mInterfaceLock) {
        // TODO: Move from decorator to direct binder-mediated exceptions
        // If setRemoteFailure already called, do nothing
        if (mInError) return;

        mRemoteDevice = new ICameraDeviceUserWrapper(remoteDevice);

        IBinder remoteDeviceBinder = remoteDevice.asBinder();
        // For legacy camera device, remoteDevice is in the same process, and
        // asBinder returns NULL.
        if (remoteDeviceBinder != null) {
            try {
                remoteDeviceBinder.linkToDeath(this, /*flag*/ 0);
            } catch (RemoteException e) {
                CameraDeviceImpl.this.mDeviceHandler.post(mCallOnDisconnected);

                throw new CameraAccessException(CameraAccessException.CAMERA_DISCONNECTED,
                        "The camera device has encountered a serious error");
            }
        }

        mDeviceHandler.post(mCallOnOpened);
        mDeviceHandler.post(mCallOnUnconfigured);
    }
}

Runtime


通過 Binder 機制,我們遠端調用了 connectDevice 方法(在 C++ 中稱爲函數,但說成方法可能更順口一些),這個方法實現在 CameraService 類中。

CameraService


文件路徑:/frameworks/av/services/camera/libcameraservice/CameraService.cpp

觀察 connectDevice 的實現:

  • 第 13~17 行,此處調用的 connectHelper 方法才真正實現了連接邏輯(HAL1 時最終也調用到這個方法)。需要注意的是,設定的模板類型是 ICameraDeviceCallbacks 以及 CameraDeviceClient。
  • 第 25 行,注意 client 指向的類型是 CameraDeviceClient,其實例則是最終的返回結果。
Status CameraService::connectDevice(
        const sp<hardware::camera2::ICameraDeviceCallbacks>& cameraCb,
        const String16& cameraId,
        const String16& clientPackageName,
        int clientUid,
        /*out*/
        sp<hardware::camera2::ICameraDeviceUser>* device) {

    ATRACE_CALL();
    Status ret = Status::ok();
    String8 id = String8(cameraId);
    sp<CameraDeviceClient> client = nullptr;
    ret = connectHelper<hardware::camera2::ICameraDeviceCallbacks,CameraDeviceClient>(cameraCb, id,
            CAMERA_HAL_API_VERSION_UNSPECIFIED, clientPackageName,
            clientUid, USE_CALLING_PID, API_2,
            /*legacyMode*/ false, /*shimUpdateOnly*/ false,
            /*out*/client);

    if(!ret.isOk()) {
        logRejected(id, getCallingPid(), String8(clientPackageName),
                ret.toString8());
        return ret;
    }

    *device = client;
    return ret;
}

connectHelper 內容較多,忽略掉我們還無需關注的地方分析:

  • 第 14~19 行,調用 makeClient 生成 CameraDeviceClient 實例。
  • 第 20~25 行,初始化 CLIENT 實例。注意此處的模板類型 CLIENT 即是 CameraDeviceClient,傳入的參數 mCameraProviderManager 則是與 HAL service 有關,這個相關內容之後再分析。
template<class CALLBACK, class CLIENT>
Status CameraService::connectHelper(const sp<CALLBACK>& cameraCb, const String8& cameraId,
        int halVersion, const String16& clientPackageName, int clientUid, int clientPid,
        apiLevel effectiveApiLevel, bool legacyMode, bool shimUpdateOnly,
        /*out*/sp<CLIENT>& device) {
    binder::Status ret = binder::Status::ok();

    String8 clientName8(clientPackageName);

    /* Do something in */
    ......
    /* Do something out */

        sp<BasicClient> tmp = nullptr;
        if(!(ret = makeClient(this, cameraCb, clientPackageName, cameraId, facing, clientPid,
                clientUid, getpid(), legacyMode, halVersion, deviceVersion, effectiveApiLevel,
                /*out*/&tmp)).isOk()) {
            return ret;
        }
        client = static_cast<CLIENT*>(tmp.get());

        LOG_ALWAYS_FATAL_IF(client.get() == nullptr, "%s: CameraService in invalid state",
                __FUNCTION__);

        err = client->initialize(mCameraProviderManager);

    /* Do something in */
    ......
    /* Do something out */

    // Important: release the mutex here so the client can call back into the service from its
    // destructor (can be at the end of the call)
    device = client;
    return ret;
}

makeClient 主要是根據 API 版本以及 HAL 版本來選擇生成具體的 Client 實例。對於 HAL3 且 CameraAPI2 的情況,請看 24~29 行,實例化了 CameraDeviceClient 類作爲 Client(注意此處構造傳入了 ICameraDeviceCallbacks,這是連接到 CameraDeviceImpl 的遠端回調) 。

最終,這一 Client 就沿着前面分析下來的路徑返回到 CameraDeviceImpl 實例中,被保存到 mRemoteDevice。 
至此,打開相機流程中,從 App 到 CameraService 的調用邏輯基本上就算走完了。
 

Status CameraService::makeClient(const sp<CameraService>& cameraService,
        const sp<IInterface>& cameraCb, const String16& packageName, const String8& cameraId,
        int facing, int clientPid, uid_t clientUid, int servicePid, bool legacyMode,
        int halVersion, int deviceVersion, apiLevel effectiveApiLevel,
        /*out*/sp<BasicClient>* client) {

    if (halVersion < 0 || halVersion == deviceVersion) {
        // Default path: HAL version is unspecified by caller, create CameraClient
        // based on device version reported by the HAL.
        switch(deviceVersion) {
          case CAMERA_DEVICE_API_VERSION_1_0:
            /* Do something in */
            ......
            /* Do something out */
          case CAMERA_DEVICE_API_VERSION_3_0:
          case CAMERA_DEVICE_API_VERSION_3_1:
          case CAMERA_DEVICE_API_VERSION_3_2:
          case CAMERA_DEVICE_API_VERSION_3_3:
          case CAMERA_DEVICE_API_VERSION_3_4:
            if (effectiveApiLevel == API_1) { // Camera1 API route
                sp<ICameraClient> tmp = static_cast<ICameraClient*>(cameraCb.get());
                *client = new Camera2Client(cameraService, tmp, packageName, cameraIdToInt(cameraId),
                        facing, clientPid, clientUid, servicePid, legacyMode);
            } else { // Camera2 API route
                sp<hardware::camera2::ICameraDeviceCallbacks> tmp =
                        static_cast<hardware::camera2::ICameraDeviceCallbacks*>(cameraCb.get());
                *client = new CameraDeviceClient(cameraService, tmp, packageName, cameraId,
                        facing, clientPid, clientUid, servicePid);
            }
            break;
          default:
            // Should not be reachable
            ALOGE("Unknown camera device HAL version: %d", deviceVersion);
            return STATUS_ERROR_FMT(ERROR_INVALID_OPERATION,
                    "Camera device \"%s\" has unknown HAL version %d",
                    cameraId.string(), deviceVersion);
        }
    } else {
        /* Do something in */
        ......
        /* Do something out */
    }
    return Status::ok();
}

簡圖總結

根據上面的流程追蹤,我們可以描繪一個比較簡單直觀的連路框架圖,如下。 
其中黑色虛線表示下行(控制)路線,紅色虛線表明上行(狀態、數據)路線。

需要注意的是:

  • CameraManagerGlobal 是真正的實現層,它與 JAVA 層的 CameraService 創建連接,從而創建相機的連路。
  • CameraDeviceImpl 相當於運行上下文,它取代了 Android N 之前的 JNI 層。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章