/* * Copyright 2021 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package androidx.camera.core; import android.content.ComponentName; import androidx.annotation.IntDef; import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.annotation.RequiresApi; import com.google.auto.value.AutoValue; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; /** * Represents the different states the camera can be in. * *
The following table displays the states the camera can be in, and the possible transitions * between them. * *
State | *Transition cause | *New State | *
---|---|---|
CLOSED | *Received signal to open camera, and camera unavailable | *PENDING_OPEN | *
Received signal to open camera, and camera available | *OPENING | *|
PENDING_OPEN | *Received signal that camera is available | *OPENING | *
OPENING | *Camera opened successfully | *OPEN | *
Camera encountered recoverable error while opening | *OPENING(Error) | *|
Camera encountered critical error while opening | *CLOSING(Error) | *|
Camera opening failed prematurely | *CLOSED(Error) | *|
Reached max limit of camera (re)open attempts | *PENDING_OPEN | *|
OPEN | *Camera encountered recoverable error | *OPENING(Error) | *
Camera encountered critical error | *CLOSING(Error) | *|
Received signal to close camera | *CLOSING | *|
CLOSING | *Camera closed | *CLOSED | *
Initially, a camera is in a {@link Type#CLOSED} state. When it receives a signal to open, for * example after one or multiple {@linkplain UseCase use cases} are attached to it, its state * moves to the {@link Type#OPENING} state. If it successfully opens the camera device, its state * moves to the {@link Type#OPEN} state, otherwise, it may move to a different state depending on * the error it encountered: * *
While in the {@link Type#PENDING_OPEN} state, the camera waits for a signal indicating the * camera device's availability. The signal can either be an external one from the camera service, * or an internal one from within CameraX. When received, the camera's state moves to the * {@link Type#OPENING} state, and an attempt to open the camera device is made. * *
While in the {@link Type#OPEN} state, the camera device may be disconnected due to an error. * In this case, depending on whether the error is critical or recoverable, CameraX may or may not * attempt to recover from it, thus the state will move to either a {@link Type#CLOSING} or * {@link Type#OPENING} state. * *
If the camera is in an {@link Type#OPEN} state and receives a signal to close the camera * device, for example when all its previously attached {@link UseCase use cases} are detached, * its state moves to the {@link Type#CLOSING} state. Once the camera device finishes closing, * the camera state moves to the {@link Type#CLOSED} state. * *
Whenever the camera encounters an error, it reports it through {@link #getError()}. */ @RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java @AutoValue public abstract class CameraState { /** * An error indicating that the limit number of open cameras has been reached, and more * cameras cannot be opened until other instances are closed. */ @SuppressWarnings("MinMaxConstant") public static final int ERROR_MAX_CAMERAS_IN_USE = 1; /** * An error indicating that the camera device is already in use. * *
This could be due to the camera device being used by a higher-priority camera client. */ public static final int ERROR_CAMERA_IN_USE = 2; /** * An error indicating that the camera device has encountered a recoverable error. * *
CameraX will attempt to recover from the error, it if succeeds in doing so, the * camera will open, otherwise the camera will move to a {@link Type#PENDING_OPEN} state. * * When CameraX uses a {@link android.hardware.camera2} implementation, this error represents * a {@link android.hardware.camera2.CameraDevice.StateCallback#ERROR_CAMERA_DEVICE} error. */ public static final int ERROR_OTHER_RECOVERABLE_ERROR = 3; /** An error indicating that configuring the camera has failed. */ public static final int ERROR_STREAM_CONFIG = 4; /** * An error indicating that the camera device could not be opened due to a device policy. * *
The error may be encountered if a client from a background process attempts to open the * camera. * * @see android.app.admin.DevicePolicyManager#setCameraDisabled(ComponentName, boolean) */ public static final int ERROR_CAMERA_DISABLED = 5; /** * An error indicating that the camera device was closed due to a fatal error. * *
The error may require the Android device to be shut down and restarted to restore camera * function. It may also indicate the existence of a persistent camera hardware problem. * * When CameraX uses a {@link android.hardware.camera2} implementation, this error represents * a {@link android.hardware.camera2.CameraDevice.StateCallback#ERROR_CAMERA_SERVICE} error. */ public static final int ERROR_CAMERA_FATAL_ERROR = 6; /** * An error indicating that the camera could not be opened because "Do Not Disturb" mode is * enabled on devices affected by a bug in Android 9 (API level 28). * *
When "Do Not Disturb" mode is enabled, opening the camera device fails on certain * Android devices running on an early Android 9 release with a * {@link android.hardware.camera2.CameraCharacteristics#INFO_SUPPORTED_HARDWARE_LEVEL_LEGACY} * camera hardware level. * *
CameraX will not attempt to reopen the camera device, instead, disable the "Do Not * Disturb" mode, then explicitly open the camera again. */ public static final int ERROR_DO_NOT_DISTURB_MODE_ENABLED = 7; /** * Create a new {@link CameraState} instance from a {@link Type} and a {@code null} * {@link StateError}. * *
A {@link CameraState} is not expected to be instantiated in normal operation. */ @NonNull public static CameraState create(@NonNull Type type) { return create(type, null); } /** * Create a new {@link CameraState} instance from a {@link Type} and a potential * {@link StateError}. * *
A {@link CameraState} is not expected to be instantiated in normal operation. */ @NonNull public static CameraState create(@NonNull Type type, @Nullable StateError error) { return new AutoValue_CameraState(type, error); } /** * Returns the camera's state. * * @return The camera's state */ @NonNull public abstract Type getType(); /** * Potentially returns an error the camera encountered. * * @return An error the camera encountered, or {@code null} otherwise. */ @Nullable public abstract StateError getError(); @IntDef(value = { ERROR_CAMERA_IN_USE, ERROR_MAX_CAMERAS_IN_USE, ERROR_OTHER_RECOVERABLE_ERROR, ERROR_STREAM_CONFIG, ERROR_CAMERA_DISABLED, ERROR_CAMERA_FATAL_ERROR, ERROR_DO_NOT_DISTURB_MODE_ENABLED}) @Retention(RetentionPolicy.SOURCE) @interface ErrorCode { } /** * Types of errors the camera can encounter. * *
CameraX tries to recover from recoverable errors, which include * {@link #ERROR_CAMERA_IN_USE}, {@link #ERROR_MAX_CAMERAS_IN_USE} and * {@link #ERROR_OTHER_RECOVERABLE_ERROR}. The rest of the errors are critical, and require * the intervention of the developer or user to restore camera function. These errors include * {@link #ERROR_STREAM_CONFIG}, {@link #ERROR_CAMERA_DISABLED}, * {@link #ERROR_CAMERA_FATAL_ERROR} and {@link #ERROR_DO_NOT_DISTURB_MODE_ENABLED}. */ public enum ErrorType { /** * An error the camera encountered that CameraX will attempt to recover from. * *
Recoverable errors include {@link #ERROR_CAMERA_IN_USE}, * {@link #ERROR_MAX_CAMERAS_IN_USE} and {@link #ERROR_OTHER_RECOVERABLE_ERROR}. */ RECOVERABLE, /** * An error the camera encountered that CameraX will not attempt to recover from. * *
A critical error is one that requires the intervention of the developer or user to * restore camera function, and includes {@link #ERROR_STREAM_CONFIG}, * {@link #ERROR_CAMERA_DISABLED}, {@link #ERROR_CAMERA_FATAL_ERROR} and * {@link #ERROR_DO_NOT_DISTURB_MODE_ENABLED}. */ CRITICAL } /** States the camera can be in. */ public enum Type { /** * Represents a state where the camera is waiting for a signal to attempt to open the camera * device. * *
The camera can move to this state from a {@link Type#CLOSED} or {@link Type#OPENING} * state: *
While in this state, the camera waits for an external signal from the camera * service or an internal one from CameraX to attempt to reopen the camera device. * *
Developers may rely on this state to close any other open cameras in the app, or * request their user close an open camera in another app. */ PENDING_OPEN, /** * Represents a state where the camera device is currently opening. * *
The camera can move to this state from a {@link Type#PENDING_OPEN} or * {@link Type#CLOSED} state: It moves to this state from a {@link Type#PENDING_OPEN} * state after it receives a signal that the camera is available to open, and from a * {@link Type#CLOSED} state after a request to open the camera is made, and the camera * is available to open. * *
While in this state, the camera is actively attempting to open the camera device. * This takes several hundred milliseconds on most devices. If it succeeds, the state * moves to the {@link Type#OPEN} state. If it fails however, the camera may attempt to * reopen the camera device a certain number of times. While this is happening, the * camera state remains the same, i.e. in an opening state, and it exposes the error it * encountered through {@link #getError()}. * *
Developers can rely on this state to be aware of when the camera is actively * attempting to open the camera device, this allows them to communicate it to their * users through the UI. */ OPENING, /** * Represents a state where the camera device is open. * *
The camera can only move to this state from an {@link Type#OPENING} state. * *
Once in this state, active {@linkplain UseCase use cases} attached to this camera * could expect to shortly start receiving camera frames. * *
Developers can rely on this state to be notified of when the camera device is actually * ready for use, and can then set up camera dependent resources, especially if they're * heavyweight. */ OPEN, /** * Represents a state where the camera device is currently closing. * *
The camera can move to this state from an {@link Type#OPEN} or {@link Type#OPENING} * state: It moves to this state from an {@link Type#OPEN} state after it receives a * signal to close the camera device, this can be after all its attached * {@linkplain UseCase use cases} are detached, and from an {@link Type#OPENING} state if * the camera encounters a fatal error it cannot recover from. * *
Developers can rely on this state to be aware of when the camera device is actually * in the process of closing. this allows them to communicate it to their users through * the UI. */ CLOSING, /** * Represents a state where the camera device is closed. * *
The camera is initially in this state, and can move back to it from a * {@link Type#CLOSING} or {@link Type#OPENING} state: It moves to this state from a * {@link Type#CLOSING} state after the camera device successfully closes, and from an * {@link Type#OPENING} state when opening a camera device that is unavailable due to * {@link android.app.NotificationManager.Policy}, as some API level 28 devices cannot * access the camera when the device is in "Do Not Disturb" mode. * *
Developers can rely on this state to be notified of when the camera device is actually * closed, and then use this signal to free up camera resources, or start the camera device * with another camera client. */ CLOSED } /** * Error that the camera has encountered. * *
The camera may report an error when it's in one of the following states: * {@link Type#OPENING}, {@link Type#OPEN}, {@link Type#CLOSING} and {@link Type#CLOSED}. * *
CameraX attempts to recover from certain errors it encounters when opening the camera * device, in these instances, the error is {@linkplain ErrorType#RECOVERABLE recoverable}, * otherwise, the error is {@linkplain ErrorType#CRITICAL critical}. * *
When CameraX encounters a critical error, the developer and/or user must intervene to * restore camera function. When the error is recoverable, the developer and/or user can * still aid in the recovery process, as shown in the following table. *
State | *Error Code | *Recoverable | *How to handle it | *
---|---|---|---|
{@link Type#OPEN} | *{@linkplain #ERROR_STREAM_CONFIG ERROR_STREAM_CONFIG} | *No | *Make sure you set up your {@linkplain UseCase use cases} correctly. | *
{@link Type#OPENING} | *{@linkplain #ERROR_CAMERA_IN_USE ERROR_CAMERA_IN_USE} | *Yes | *Close the camera, or ask the user to close another camera app that is using the * camera. | *
{@link Type#OPENING} | *{@linkplain #ERROR_MAX_CAMERAS_IN_USE ERROR_MAX_CAMERAS_IN_USE} | *Yes | *Close another open camera in the app, or ask the user to close another camera * app that's using the camera. | *
{@link Type#OPENING} | *{@linkplain #ERROR_OTHER_RECOVERABLE_ERROR ERROR_OTHER_RECOVERABLE_ERROR} | *Yes | *N/A | *
{@link Type#CLOSING} | *{@linkplain #ERROR_CAMERA_DISABLED ERROR_CAMERA_DISABLED} | *No | *Ask the user to enable the device's cameras. | *
{@link Type#CLOSING} | *{@linkplain #ERROR_CAMERA_FATAL_ERROR ERROR_CAMERA_FATAL_ERROR} | *No | *Ask the user to reboot the device to restore camera function. | *
{@link Type#CLOSED} | *{@linkplain #ERROR_DO_NOT_DISTURB_MODE_ENABLED * ERROR_DO_NOT_DISTURB_MODE_ENABLED} | *No | *Ask the user to disable "Do Not Disturb" mode, then open the camera again. | *
A {@link StateError} is not expected to be instantiated in normal operation. */ @NonNull public static StateError create(@ErrorCode int error) { return create(error, null); } /** * Creates a {@link StateError} with an error code and a {@linkplain Throwable cause}. * *
A {@link StateError} is not expected to be instantiated in normal operation. */ @NonNull public static StateError create(@ErrorCode int error, @Nullable Throwable cause) { return new AutoValue_CameraState_StateError(error, cause); } /** * Returns the code of this error. * *
The error's code is one of the following: {@link #ERROR_CAMERA_IN_USE}, * {@link #ERROR_MAX_CAMERAS_IN_USE}, {@link #ERROR_OTHER_RECOVERABLE_ERROR}, * {@link #ERROR_STREAM_CONFIG}, {@link #ERROR_CAMERA_DISABLED}, * {@link #ERROR_CAMERA_FATAL_ERROR} and {@link #ERROR_DO_NOT_DISTURB_MODE_ENABLED}. * * @return The code of this error. */ @ErrorCode public abstract int getCode(); /** * Returns a potential cause of this error. * * @return The cause of this error, or {@code null} if the cause was not supplied. */ @Nullable public abstract Throwable getCause(); /** * Returns the type of this error. * *
An error can either be {@linkplain ErrorType#RECOVERABLE recoverable} or * {@linkplain ErrorType#CRITICAL critical}. * * @return The type of this error */ @NonNull public ErrorType getType() { int code = getCode(); if (code == ERROR_CAMERA_IN_USE || code == ERROR_MAX_CAMERAS_IN_USE || code == ERROR_OTHER_RECOVERABLE_ERROR) { return ErrorType.RECOVERABLE; } return ErrorType.CRITICAL; } } }