【camera-framework 002】从Java应用打开一个相机的全流程 1

这里追踪 相机打开流程,从 Java API 到 camera service,可以参照下面的核心类,进行追踪
在这里插入图片描述

1 openCamera

frameworks/base/core/java/android/hardware/camera2/CameraManager.java # openCamera
打开与给定ID的相机的连接。
下面是一些注意点,简单看一下:

使用{@link #getCameraIdList}来获取可用相机设备的列表。请注意,即使列出了id,如果设备在调用{@ link# getCameraIdList}和{@ link# openCamera}之间断开连接,或者如果更高优先级的相机API客户端开始使用相机设备,则打开可能会失败。

一旦相机成功打开,{@link CameraDevice。StateCallback#onOpened}将与新打开的{@link CameraDevice}一起调用。然后可以通过调用{@link CameraDevice#createCaptureSession}和{@link CameraDevice#createCaptureRequest}来设置相机设备进行操作。

在API级别30之前,当应用程序尝试打开多个不同id的{@link CameraDevice}并且设备不支持打开这样的组合时,{@link #openCamera}将失败并抛出{@link CameraAccessException}或一个或多个已打开的{@link CameraDevice}将断开连接并接收{@link android.硬件。camera2.CameraDevice.StateCallback # onDisconnected}的回调函数。哪种行为会发生取决于设备实现,并且在不同的设备上可能会有所不同。从API级别30开始,如果设备不支持正在打开的相机组合,则可以保证{@link #openCamera}调用将失败,并且现有的{@link CameraDevice}都不会断开连接。

由于相机设备将被异步打开,任何在返回的CameraDevice实例上完成的异步操作将被排队,直到设备启动完成和回调的{@link CameraDevice.StateCallback#onOpened onOpened}方法被调用。然后按顺序处理挂起的操作。

  * @param cameraId
  *             The unique identifier of the camera device to open
  * @param callback
  *             The callback which is invoked once the camera is opened
  * @param handler
  *             The handler on which the callback should be invoked, or
  *             {@code null} to use the current thread's {@link android.os.Looper looper}.
// Open a connection to a camera with the given ID.

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

     openCameraForUid(cameraId, callback, CameraDeviceImpl.checkAndWrapHandler(handler),
             USE_CALLING_UID);
 }
 
  

2 追踪 openCamera 具体做了哪些事情

我们重点看一下 打开相机的过程做了哪些事情:

2.1 openCameraForUid

可以看到 openCamera指定用于权限等验证的UID为 USE_CALLING_UID,用来通过权限认证

/**
     * Open a connection to a camera with the given ID, on behalf of another application
     * specified by clientUid.
     代表由clientUid指定的另一个应用程序,打开给定ID的相机的连接。
     *
     * <p>The behavior of this method matches that of {@link #openCamera}, except that it allows
     * the caller to specify the UID to use for permission/etc verification. This can only be
     * done by services trusted by the camera subsystem to act on behalf of applications and
     * to forward the real UID.</p>
     *
     * @param clientUid
     *             The UID of the application on whose behalf the camera is being opened.
     *             Must be USE_CALLING_UID unless the caller is a trusted service.
     *
     * @hide
     */
    public void openCameraForUid(@NonNull String cameraId,
            @NonNull final CameraDevice.StateCallback callback, @NonNull Executor executor,
            int clientUid) throws CameraAccessException {
        openCameraForUid(cameraId, callback, executor, clientUid, /*oomScoreOffset*/0,
                shouldOverrideToPortrait(mContext));
    }

2.2 openCameraDeviceUserAsync

frameworks/base/core/java/android/hardware/camera2/CameraManager.java # openCameraDeviceUserAsync
真正的逻辑在这里开始


    /**
     * Helper for opening a connection to a camera with the given ID.
     *
     * @param cameraId The unique identifier of the camera device to open
     * @param callback The callback for the camera. Must not be null.
     * @param executor The executor to invoke the callback with. Must not be null.
     * @param uid      The UID of the application actually opening the camera.
     *                 Must be USE_CALLING_UID unless the caller is a service
     *                 that is trusted to open the device on behalf of an
     *                 application and to forward the real UID.
     *
     * @throws CameraAccessException if the camera is disabled by device policy,
     * too many camera devices are already open, or the cameraId does not match
     * any currently available camera device.
     *
     * @throws SecurityException if the application does not have permission to
     * access the camera
     * @throws IllegalArgumentException if callback or handler is null.
     * @return A handle to the newly-created camera device.
     *
     * @see #getCameraIdList
     * @see android.app.admin.DevicePolicyManager#setCameraDisabled
     */
    private CameraDevice openCameraDeviceUserAsync(String cameraId,
            CameraDevice.StateCallback callback, Executor executor, final int uid,
            final int oomScoreOffset, boolean overrideToPortrait) throws CameraAccessException {
          // 返回的信息,包括是否有强制并发流的标志,int 类型的camera id, display size, 可能得 多分辨率流配置       
        CameraCharacteristics characteristics = getCameraCharacteristics(cameraId);
        
        CameraDevice device = null;
        // 返回 getPhysicalCameraIds()返回的physicalCameraId作为key, 
        // getCameraCharacteristics(physicalCameraId)返回的 CameraCharacteristics 作为 value的 map
        Map<String, CameraCharacteristics> physicalIdsToChars =
                getPhysicalIdToCharsMap(characteristics);
        synchronized (mLock) {

            ICameraDeviceUser cameraUser = null;
            android.hardware.camera2.impl.CameraDeviceImpl deviceImpl =
                    new android.hardware.camera2.impl.CameraDeviceImpl(
                        cameraId,
                        callback, // CameraDeviceImpl # mDeviceCallback
                        executor,
                        characteristics,
                        physicalIdsToChars,
                        mContext.getApplicationInfo().targetSdkVersion,
                        mContext);
            // CameraDeviceImpl # mCallbacks                                        
            ICameraDeviceCallbacks callbacks = deviceImpl.getCallbacks();

            try {
                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(), mContext.getAttributionTag(), uid,
                    oomScoreOffset, mContext.getApplicationInfo().targetSdkVersion,
                    overrideToPortrait);
            } catch (ServiceSpecificException e) {
                if (e.errorCode == ICameraService.ERROR_DEPRECATED_HAL) {
                    throw new AssertionError("Should've gone down the shim path");
                } else if (e.errorCode == ICameraService.ERROR_CAMERA_IN_USE ||
                        e.errorCode == ICameraService.ERROR_MAX_CAMERAS_IN_USE ||
                        e.errorCode == ICameraService.ERROR_DISABLED ||
                        e.errorCode == ICameraService.ERROR_DISCONNECTED ||
                        e.errorCode == ICameraService.ERROR_INVALID_OPERATION) {
                    // Received one of the known connection errors
                    // The remote camera device cannot be connected to, so
                    // set the local camera to the startup error state
                    deviceImpl.setRemoteFailure(e);

                    if (e.errorCode == ICameraService.ERROR_DISABLED ||
                            e.errorCode == ICameraService.ERROR_DISCONNECTED ||
                            e.errorCode == ICameraService.ERROR_CAMERA_IN_USE) {
                        // Per API docs, these failures call onError and throw
                        throwAsPublicException(e);
                    }
                } else {
                    // Unexpected failure - rethrow
                    throwAsPublicException(e);
                }
            } catch (RemoteException e) {
                // Camera service died - act as if it's a CAMERA_DISCONNECTED case
                ServiceSpecificException sse = new ServiceSpecificException(
                    ICameraService.ERROR_DISCONNECTED,
                    "Camera service is currently unavailable");
                deviceImpl.setRemoteFailure(sse);
                throwAsPublicException(sse);
            }

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

2.2.1 getCameraCharacteristics

返回的信息,包括是否有强制并发流,int 类型的camera id, display size, 可能得 多分辨率流配置;当然还有其他的属性,这里不关注
frameworks/base/core/java/android/hardware/camera2/CameraManager.java # getCameraCharacteristics

 /**
     * <p>Query the capabilities of a camera device. These capabilities are
     * immutable for a given camera.</p>
     *
     * <p>From API level 29, this function can also be used to query the capabilities of physical
     * cameras that can only be used as part of logical multi-camera. These cameras cannot be
     * opened directly via {@link #openCamera}</p>
     *
     * <p>Also starting with API level 29, while most basic camera information is still available
     * even without the CAMERA permission, some values are not available to apps that do not hold
     * that permission. The keys not available are listed by
     * {@link CameraCharacteristics#getKeysNeedingPermission}.</p>
     *
     * @param cameraId The id of the camera device to query. This could be either a standalone
     * camera ID which can be directly opened by {@link #openCamera}, or a physical camera ID that
     * can only used as part of a logical multi-camera.
     * @return The properties of the given camera
     *
     * @throws IllegalArgumentException if the cameraId does not match any
     *         known camera device.
     * @throws CameraAccessException if the camera device has been disconnected.
     *
     * @see #getCameraIdList
     * @see android.app.admin.DevicePolicyManager#setCameraDisabled
     */
    @NonNull
    public CameraCharacteristics getCameraCharacteristics(@NonNull String cameraId)
            throws CameraAccessException {
        return getCameraCharacteristics(cameraId, shouldOverrideToPortrait(mContext));
    }
    
    public CameraCharacteristics getCameraCharacteristics(@NonNull String cameraId,
            boolean overrideToPortrait) throws CameraAccessException {
        CameraCharacteristics characteristics = null;
        if (CameraManagerGlobal.sCameraServiceDisabled) {
            throw new IllegalArgumentException("No cameras available on device");
        }
        synchronized (mLock) {
            ICameraService cameraService = CameraManagerGlobal.get().getCameraService();
            if (cameraService == null) {
                throw new CameraAccessException(CameraAccessException.CAMERA_DISCONNECTED,
                        "Camera service is currently unavailable");
            }
            try {
                Size displaySize = getDisplaySize();

                CameraMetadataNative info = cameraService.getCameraCharacteristics(cameraId,
                        mContext.getApplicationInfo().targetSdkVersion, overrideToPortrait);
                try {
                    info.setCameraId(Integer.parseInt(cameraId));
                } catch (NumberFormatException e) {
                    Log.v(TAG, "Failed to parse camera Id " + cameraId + " to integer");
                }

                boolean hasConcurrentStreams =
                        CameraManagerGlobal.get().cameraIdHasConcurrentStreamsLocked(cameraId);
                info.setHasMandatoryConcurrentStreams(hasConcurrentStreams);
                info.setDisplaySize(displaySize);

                Map<String, StreamConfiguration[]> multiResolutionSizeMap =
                        getPhysicalCameraMultiResolutionConfigs(cameraId, info, cameraService);
                if (multiResolutionSizeMap.size() > 0) {
                    info.setMultiResolutionStreamConfigurationMap(multiResolutionSizeMap);
                }
                
                characteristics = new CameraCharacteristics(info);
            } catch (ServiceSpecificException e) {
                throw ExceptionUtils.throwAsPublicException(e);
            } catch (RemoteException e) {
                // Camera service died - act as if the camera was disconnected
                throw new CameraAccessException(CameraAccessException.CAMERA_DISCONNECTED,
                        "Camera service is currently unavailable", e);
            }
        }
        registerDeviceStateListener(characteristics);
        // 返回的信息,包括是否有强制并发流,int 类型的camera id, display size, 可能得 多分辨率流配置
        return characteristics;
    }
CameraCharacteristics

frameworks/base/core/java/android/hardware/camera2/CameraCharacteristics.java

/**
     * Takes ownership of the passed-in properties object
     *
     * @param properties Camera properties.
     * @hide
     */
    public CameraCharacteristics(CameraMetadataNative properties) {
        mProperties = CameraMetadataNative.move(properties);
        setNativeInstance(mProperties);
    }
CameraMetadataNative

实现相机元数据跨Binder到camera service的编组/解组
frameworks/base/core/java/android/hardware/camera2/impl/CameraMetadataNative.java

public class CameraMetadataNative implements Parcelable {
    /**
     * Set the current camera Id.
     *
     * @param cameraId Current camera id.
     *
     * @hide
     */
    public void setCameraId(int cameraId) {
        mCameraId = cameraId;
    }
    
    /**
     * Set the current camera Id.
     *
     * @param hasMandatoryConcurrentStreams whether the metadata advertises mandatory concurrent
     *        streams.
     hasMandatoryConcurrentStreams元数据是否发布强制并发流。
     *
     * @hide
     */
    public void setHasMandatoryConcurrentStreams(boolean hasMandatoryConcurrentStreams) {
        mHasMandatoryConcurrentStreams = hasMandatoryConcurrentStreams;
    }
    
     /**
     * Set the current display size.
     *
     * @param displaySize The current display size.
     *
     * @hide
     */
    public void setDisplaySize(Size displaySize) {
        mDisplaySize = displaySize;
    }
    
    /**
     * Set the multi-resolution stream configuration map.
     *
     * @param multiResolutionMap The multi-resolution stream configuration map.
     *
     * @hide
     */
    public void setMultiResolutionStreamConfigurationMap(
            @NonNull Map<String, StreamConfiguration[]> multiResolutionMap) {
        mMultiResolutionStreamConfigurationMap =
                new MultiResolutionStreamConfigurationMap(multiResolutionMap);
    }
}

2.2.2 getPhysicalIdToCharsMap

frameworks/base/core/java/android/hardware/camera2/CameraManager.java # getPhysicalIdToCharsMap
返回 getPhysicalCameraIds()返回的物理相机id作为key, getCameraCharacteristics(physicalCameraId)返回的 CameraCharacteristics 作为 value的 map

private Map<String, CameraCharacteristics> getPhysicalIdToCharsMap(
            CameraCharacteristics chars) throws CameraAccessException {
        HashMap<String, CameraCharacteristics> physicalIdsToChars =
                new HashMap<String, CameraCharacteristics>();
                // 返回由这个逻辑相机{@link CameraDevice}组成的物理相机id集合                
        Set<String> physicalCameraIds = chars.getPhysicalCameraIds();
        for (String physicalCameraId : physicalCameraIds) {
            CameraCharacteristics physicalChars = getCameraCharacteristics(physicalCameraId);
            physicalIdsToChars.put(physicalCameraId, physicalChars);
        }
        return physicalIdsToChars;
    }

CameraCharacteristics.java # getPhysicalCameraIds
返回由这个逻辑相机{@link CameraDevice}组成的物理相机id集合
frameworks/base/core/java/android/hardware/camera2/CameraCharacteristics.java # getPhysicalCameraIds

在API级别29之前,所有返回的id都保证由{@link CameraManager#getCameraIdList}返回,并且可以通过{@link CameraManager#openCamera}直接打开。
从API级别29开始,对于每个返回的ID,如果它也由{@link CameraManager#getCameraIdList}返回,它可以被{@link CameraManager#openCamera}用作一个独立的相机。否则,摄像机ID只能作为当前逻辑摄像机的一部分使用。

/**
     * Returns the set of physical camera ids that this logical {@link CameraDevice} is
     * made up of.
     *
     * <p>The set returned is not modifiable, so any attempts to modify it will throw
     * a {@code UnsupportedOperationException}.</p>
     *
     * @return Set of physical camera ids for this logical camera device.
     */    
     @NonNull    
     public Set<String> getPhysicalCameraIds() {
        return mProperties.getPhysicalCameraIds();
    }

2…2.3 CameraDeviceImpl

frameworks/base/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java
/**

  • HAL2.1+ implementation of CameraDevice. Use CameraManager#open to instantiate
    */
public CameraDeviceImpl(String cameraId, StateCallback callback, Executor executor,
                        CameraCharacteristics characteristics,
                        Map<String, CameraCharacteristics> physicalIdsToChars,
                        int appTargetSdkVersion,
                        Context ctx) {
        if (cameraId == null || callback == null || executor == null || characteristics == null) {
            throw new IllegalArgumentException("Null argument given");
        }
        mCameraId = cameraId;
        mDeviceCallback = callback;
        mDeviceExecutor = executor;
        mCharacteristics = characteristics;
        mPhysicalIdsToChars = physicalIdsToChars;
        mAppTargetSdkVersion = appTargetSdkVersion;
        mContext = ctx;

        final int MAX_TAG_LEN = 23;
        String tag = String.format("CameraDevice-JV-%s", mCameraId);
        if (tag.length() > MAX_TAG_LEN) {
            tag = tag.substring(0, MAX_TAG_LEN);
        }
        TAG = tag;

        Integer partialCount =
                mCharacteristics.get(CameraCharacteristics.REQUEST_PARTIAL_RESULT_COUNT);
        if (partialCount == null) {
            // 1 means partial result is not supported.
            mTotalPartialCount = 1;
        } else {
            mTotalPartialCount = partialCount;
        }
    }
2.2.3.1 getCallbacks()

frameworks/base/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java # getCallbacks()

private final CameraDeviceCallbacks  mCallbacks = new  CameraDeviceCallbacks();

public CameraDeviceCallbacks getCallbacks() {
        return mCallbacks;
    }

frameworks/base/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java # CameraDeviceCallbacks

 /**
     * A callback for notifications about the state of a camera device, adding in the callbacks that
     * were part of the earlier KK API design, but now only used internally.
     */    
public class CameraDeviceCallbacks extends ICameraDeviceCallbacks.Stub {
    
}}

2.3 打开 camera device

经过追踪,和前面的参数、回调、权限等准备,最终来到真正打开相机的逻辑入口

//callbacks 实际上是  CameraDeviceImpl # mCallbacks   
// frameworks/base/core/java/android/hardware/camera2/CameraManager.java # openCameraDeviceUserAsync
cameraUser = cameraService.connectDevice(callbacks, cameraId,
                    mContext.getOpPackageName(), mContext.getAttributionTag(), uid,
                    oomScoreOffset, mContext.getApplicationInfo().targetSdkVersion,
                    overrideToPortrait);

ICameraService.aidl
通过aidl 接口,最终会调用到CameraService 对应的接口,这里就跨进程了,下面就是 native framework的代码,暂时先追踪到这里,下次从CameraService往下追踪到HAL

 /**
     * Open a camera device through the new camera API
     * Only supported for device HAL versions >= 3.2
     */
    ICameraDeviceUser connectDevice(ICameraDeviceCallbacks callbacks,
            @utf8InCpp String cameraId,
            @utf8InCpp String opPackageName,
            @nullable @utf8InCpp String featureId,
            int clientUid, int oomScoreOffset,
            int targetSdkVersion,
            boolean overrideToPortrait);
  • 17
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值