Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 1636af3a authored by Jayant Chowdhary's avatar Jayant Chowdhary Committed by Android (Google) Code Review
Browse files

Merge "camera2: Add apis for querying concurrent streaming support."

parents 344d21a8 a24f94b1
Loading
Loading
Loading
Loading
+4 −0
Original line number Diff line number Diff line
@@ -12017,6 +12017,7 @@ package android.content.pm {
    field public static final String FEATURE_CAMERA_CAPABILITY_MANUAL_POST_PROCESSING = "android.hardware.camera.capability.manual_post_processing";
    field public static final String FEATURE_CAMERA_CAPABILITY_MANUAL_SENSOR = "android.hardware.camera.capability.manual_sensor";
    field public static final String FEATURE_CAMERA_CAPABILITY_RAW = "android.hardware.camera.capability.raw";
    field public static final String FEATURE_CAMERA_CONCURRENT = "android.hardware.camera.concurrent";
    field public static final String FEATURE_CAMERA_EXTERNAL = "android.hardware.camera.external";
    field public static final String FEATURE_CAMERA_FLASH = "android.hardware.camera.flash";
    field public static final String FEATURE_CAMERA_FRONT = "android.hardware.camera.front";
@@ -17174,6 +17175,7 @@ package android.hardware.camera2 {
    field @NonNull public static final android.hardware.camera2.CameraCharacteristics.Key<java.lang.Float> SCALER_AVAILABLE_MAX_DIGITAL_ZOOM;
    field @NonNull public static final android.hardware.camera2.CameraCharacteristics.Key<int[]> SCALER_AVAILABLE_ROTATE_AND_CROP_MODES;
    field @NonNull public static final android.hardware.camera2.CameraCharacteristics.Key<java.lang.Integer> SCALER_CROPPING_TYPE;
    field @NonNull public static final android.hardware.camera2.CameraCharacteristics.Key<android.hardware.camera2.params.MandatoryStreamCombination[]> SCALER_MANDATORY_CONCURRENT_STREAM_COMBINATIONS;
    field @NonNull public static final android.hardware.camera2.CameraCharacteristics.Key<android.hardware.camera2.params.MandatoryStreamCombination[]> SCALER_MANDATORY_STREAM_COMBINATIONS;
    field @NonNull public static final android.hardware.camera2.CameraCharacteristics.Key<android.hardware.camera2.params.StreamConfigurationMap> SCALER_STREAM_CONFIGURATION_MAP;
    field @NonNull public static final android.hardware.camera2.CameraCharacteristics.Key<int[]> SENSOR_AVAILABLE_TEST_PATTERN_MODES;
@@ -17263,6 +17265,8 @@ package android.hardware.camera2 {
  public final class CameraManager {
    method @NonNull public android.hardware.camera2.CameraCharacteristics getCameraCharacteristics(@NonNull String) throws android.hardware.camera2.CameraAccessException;
    method @NonNull public String[] getCameraIdList() throws android.hardware.camera2.CameraAccessException;
    method @NonNull public java.util.Set<java.util.Set<java.lang.String>> getConcurrentStreamingCameraIds() throws android.hardware.camera2.CameraAccessException;
    method @RequiresPermission(android.Manifest.permission.CAMERA) public boolean isConcurrentSessionConfigurationSupported(@NonNull java.util.Map<java.lang.String,android.hardware.camera2.params.SessionConfiguration>) throws android.hardware.camera2.CameraAccessException;
    method @RequiresPermission(android.Manifest.permission.CAMERA) public void openCamera(@NonNull String, @NonNull android.hardware.camera2.CameraDevice.StateCallback, @Nullable android.os.Handler) throws android.hardware.camera2.CameraAccessException;
    method @RequiresPermission(android.Manifest.permission.CAMERA) public void openCamera(@NonNull String, @NonNull java.util.concurrent.Executor, @NonNull android.hardware.camera2.CameraDevice.StateCallback) throws android.hardware.camera2.CameraAccessException;
    method public void registerAvailabilityCallback(@NonNull android.hardware.camera2.CameraManager.AvailabilityCallback, @Nullable android.os.Handler);
+9 −0
Original line number Diff line number Diff line
@@ -1958,6 +1958,15 @@ public abstract class PackageManager {
    public static final String FEATURE_CAMERA_AR =
            "android.hardware.camera.ar";

    /**
     * Feature for {@link #getSystemAvailableFeatures} and
     * {@link #hasSystemFeature}: The device's main front and back cameras can stream
     * concurrently as described in  {@link
     * android.hardware.camera2.CameraManager#getConcurrentStreamingCameraIds()}
     */
    @SdkConstant(SdkConstantType.FEATURE)
    public static final String FEATURE_CAMERA_CONCURRENT = "android.hardware.camera.concurrent";

    /**
     * Feature for {@link #getSystemAvailableFeatures} and
     * {@link #hasSystemFeature}: The device is capable of communicating with
+22 −1
Original line number Diff line number Diff line
@@ -2875,7 +2875,28 @@ public final class CameraCharacteristics extends CameraMetadata<CameraCharacteri
    @NonNull
    public static final Key<int[]> SCALER_AVAILABLE_ROTATE_AND_CROP_MODES =
            new Key<int[]>("android.scaler.availableRotateAndCropModes", int[].class);

    /**
     * <p>An array of mandatory concurrent stream combinations.
     * This is an app-readable conversion of the concurrent mandatory stream combination
     * {@link android.hardware.camera2.CameraDevice#createCaptureSession tables}.</p>
     * <p>The array of
     * {@link android.hardware.camera2.params.MandatoryStreamCombination combinations} is
     * generated according to the documented
     * {@link android.hardware.camera2.CameraDevice#createCaptureSession guideline} for each device
     * which has its Id present in the set returned by
     * {@link android.hardware.camera2.CameraManager#getConcurrentStreamingCameraIds}.
     * Clients can use the array as a quick reference to find an appropriate camera stream
     * combination.
     * The mandatory stream combination array will be {@code null} in case the device is not a part
     * of at least one set of combinations returned by
     * {@link android.hardware.camera2.CameraManager#getConcurrentStreamingCameraIds}.</p>
     * <p><b>Optional</b> - The value for this key may be {@code null} on some devices.</p>
     */
    @PublicKey
    @NonNull
    @SyntheticKey
    public static final Key<android.hardware.camera2.params.MandatoryStreamCombination[]> SCALER_MANDATORY_CONCURRENT_STREAM_COMBINATIONS =
            new Key<android.hardware.camera2.params.MandatoryStreamCombination[]>("android.scaler.mandatoryConcurrentStreamCombinations", android.hardware.camera2.params.MandatoryStreamCombination[].class);
    /**
     * <p>The area of the image sensor which corresponds to active pixels after any geometric
     * distortion correction has been applied.</p>
+19 −0
Original line number Diff line number Diff line
@@ -680,6 +680,25 @@ public abstract class CameraDevice implements AutoCloseable {
     * </table><br>
     * </p>
     *
     *<p>Devices capable of streaming concurrently with other devices as described by
     * {@link android.hardware.camera2.CameraManager#getConcurrentStreamingCameraIds} have the
     * following guaranteed streams (when streaming concurrently with other devices)</p>
     *
     * <table>
     * <tr><th colspan="5">Concurrent stream guaranteed configurations</th></tr>
     * <tr><th colspan="2" id="rb">Target 1</th><th colspan="2" id="rb">Target 2</th><th rowspan="2">Sample use case(s)</th> </tr>
     * <tr><th>Type</th><th id="rb">Max size</th><th>Type</th><th id="rb">Max size</th> </tr>
     * <tr> <td>{@code YUV}</td><td id="rb">{@code MAXIMUM}</td>  <td colspan="2" id="rb"></td> <td>In-app video / image processing.</td> </tr>
     * <tr> <td>{@code PRIV}</td><td id="rb">{@code MAXIMUM}</td>  <td colspan="2" id="rb"></td> <td>In-app viewfinder analysis.</td> </tr>
     * <tr> <td>{@code YUV}</td><td id="rb">{@code MAXIMUM}</td> <td>{@code YUV }</td><td id="rb">{@code MAXIMUM}</td> <td>In-app video / processing with preview.</td> </tr>
     * <tr> <td>{@code YUV}</td><td id="rb">{@code MAXIMUM}</td> <td>{@code PRIV }</td><td id="rb">{@code MAXIMUM}</td> <td>In-app video / processing with preview.</td> </tr>
     * <tr> <td>{@code PRIV}</td><td id="rb">{@code MAXIMUM}</td> <td>{@code PRIV }</td><td id="rb">{@code MAXIMUM}</td> <td>Standard Recording.</td> </tr>
     * </table><br>
     * </p>
     *
     * <p> For guaranteed concurrent stream configurations, MAXIMUM refers to the camera device's
     * resolution for that format from {@link StreamConfigurationMap#getOutputSizes} or
     * 720p(1280X720) whichever is lower. </p>
     * <p>MONOCHROME-capability ({@link CameraCharacteristics#REQUEST_AVAILABLE_CAPABILITIES}
     * includes {@link CameraMetadata#REQUEST_AVAILABLE_CAPABILITIES_MONOCHROME MONOCHROME}) devices
     * supporting {@link android.graphics.ImageFormat#Y8 Y8} support substituting {@code YUV}
+195 −1
Original line number Diff line number Diff line
@@ -31,6 +31,9 @@ import android.hardware.camera2.impl.CameraDeviceImpl;
import android.hardware.camera2.impl.CameraMetadataNative;
import android.hardware.camera2.legacy.CameraDeviceUserShim;
import android.hardware.camera2.legacy.LegacyMetadataMapper;
import android.hardware.camera2.params.SessionConfiguration;
import android.hardware.camera2.utils.CameraIdAndSessionConfiguration;
import android.hardware.camera2.utils.ConcurrentCameraIdCombination;
import android.os.Binder;
import android.os.DeadObjectException;
import android.os.Handler;
@@ -40,6 +43,7 @@ import android.os.ServiceManager;
import android.os.ServiceSpecificException;
import android.os.SystemProperties;
import android.util.ArrayMap;
import android.util.ArraySet;
import android.util.Log;
import android.util.Size;
import android.view.Display;
@@ -48,6 +52,7 @@ import android.view.WindowManager;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Comparator;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.Executor;
import java.util.concurrent.Executors;
@@ -125,6 +130,66 @@ public final class CameraManager {
        return CameraManagerGlobal.get().getCameraIdListNoLazy();
    }

    /**
     * Return the list of combinations of currently connected camera devices identifiers, which
     * support configuring camera device sessions concurrently.
     *
     * <p>The set of combinations may include camera devices that may be in use by other camera API
     * clients.</p>
     *
     * <p>The set of combinations doesn't contain physical cameras that can only be used as
     * part of a logical multi-camera device.</p>
     *
     * @return The set of combinations of currently connected camera devices, that may have
     *         sessions configured concurrently. The set of combinations will be empty if no such
     *         combinations are supported by the camera subsystem.
     *
     * @throws CameraAccessException if the camera device has been disconnected.
     */
    @NonNull
    public Set<Set<String>> getConcurrentStreamingCameraIds() throws CameraAccessException {
        return CameraManagerGlobal.get().getConcurrentStreamingCameraIds();
    }

    /**
     * Checks whether the provided set of camera devices and their corresponding
     * {@link SessionConfiguration} can be configured concurrently.
     *
     * <p>This method performs a runtime check of the given {@link SessionConfiguration} and camera
     * id combinations. The result confirms whether or not the passed session configurations can be
     * successfully used to create camera capture sessions concurrently, on the given camera
     * devices using {@link CameraDevice#createCaptureSession(SessionConfiguration)}.
     * </p>
     *
     * <p>The method can be called at any point before, during and after active capture sessions.
     * It will not impact normal camera behavior in any way and must complete significantly
     * faster than creating a regular or constrained capture session.</p>
     *
     * <p>Although this method is faster than creating a new capture session, it is not intended
     * to be used for exploring the entire space of supported concurrent stream combinations. The
     * available mandatory concurrent stream combinations may be obtained by querying
     * {@link #getCameraCharacteristics} for the key
     * SCALER_MANDATORY_CONCURRENT_STREAM_COMBINATIONS. </p>
     *
     * <p>Note that session parameters will be ignored and calls to
     * {@link SessionConfiguration#setSessionParameters} are not required.</p>
     *
     * @return {@code true} if the given combination of session configurations and corresponding
     *                      camera ids are concurrently supported by the camera sub-system,
     *         {@code false} otherwise.
     *
     * @throws IllegalArgumentException if the set of camera devices provided is not a subset of
     *                                  those returned by getConcurrentStreamingCameraIds()
     * @throws CameraAccessException if one of the camera devices queried is no longer connected.
     */
    @RequiresPermission(android.Manifest.permission.CAMERA)
    public boolean isConcurrentSessionConfigurationSupported(
            @NonNull Map<String, SessionConfiguration> cameraIdAndSessionConfig)
            throws CameraAccessException {
        return CameraManagerGlobal.get().isConcurrentSessionConfigurationSupported(
                cameraIdAndSessionConfig);
    }

    /**
     * Register a callback to be notified about camera device availability.
     *
@@ -336,8 +401,10 @@ public final class CameraManager {
                    } catch (NumberFormatException e) {
                        Log.e(TAG, "Failed to parse camera Id " + cameraId + " to integer");
                    }
                    boolean hasConcurrentStreams =
                            CameraManagerGlobal.get().cameraIdHasConcurrentStreamsLocked(cameraId);
                    info.setHasMandatoryConcurrentStreams(hasConcurrentStreams);
                    info.setDisplaySize(displaySize);

                    characteristics = new CameraCharacteristics(info);
                }
            } catch (ServiceSpecificException e) {
@@ -964,6 +1031,9 @@ public final class CameraManager {
        private final ArrayMap<String, ArrayList<String>> mUnavailablePhysicalDevices =
                new ArrayMap<String, ArrayList<String>>();

        private final Set<Set<String>> mConcurrentCameraIdCombinations =
                new ArraySet<Set<String>>();

        // Registered availablility callbacks and their executors
        private final ArrayMap<AvailabilityCallback, Executor> mCallbackMap =
            new ArrayMap<AvailabilityCallback, Executor>();
@@ -1068,7 +1138,22 @@ public final class CameraManager {
            } catch (RemoteException e) {
                // Camera service is now down, leave mCameraService as null
            }

            try {
                ConcurrentCameraIdCombination[] cameraIdCombinations =
                        cameraService.getConcurrentStreamingCameraIds();
                for (ConcurrentCameraIdCombination comb : cameraIdCombinations) {
                    mConcurrentCameraIdCombinations.add(comb.getConcurrentCameraIdCombination());
                }
            } catch (ServiceSpecificException e) {
                // Unexpected failure
                throw new IllegalStateException("Failed to get concurrent camera id combinations",
                        e);
            } catch (RemoteException e) {
                // Camera service died in all probability
            }
        }

        private String[] extractCameraIdListLocked() {
            String[] cameraIds = null;
            int idCount = 0;
@@ -1089,6 +1174,31 @@ public final class CameraManager {
            }
            return cameraIds;
        }

        private Set<Set<String>> extractConcurrentCameraIdListLocked() {
            Set<Set<String>> concurrentCameraIds = new ArraySet<Set<String>>();
            for (Set<String> cameraIds : mConcurrentCameraIdCombinations) {
                Set<String> extractedCameraIds = new ArraySet<String>();
                for (String cameraId : cameraIds) {
                    // if the camera id status is NOT_PRESENT or ENUMERATING; skip the device.
                    // TODO: Would a device status NOT_PRESENT ever be in the map ? it gets removed
                    // in the callback anyway.
                    Integer status = mDeviceStatus.get(cameraId);
                    if (status == null) {
                        // camera id not present
                        continue;
                    }
                    if (status == ICameraServiceListener.STATUS_ENUMERATING
                            || status == ICameraServiceListener.STATUS_NOT_PRESENT) {
                        continue;
                    }
                    extractedCameraIds.add(cameraId);
                }
                concurrentCameraIds.add(extractedCameraIds);
            }
            return concurrentCameraIds;
        }

        private static void sortCameraIds(String[] cameraIds) {
            // The sort logic must match the logic in
            // libcameraservice/common/CameraProviderManager.cpp::getAPI1CompatibleCameraDeviceIds
@@ -1214,6 +1324,88 @@ public final class CameraManager {
            return cameraIds;
        }

        public @NonNull Set<Set<String>> getConcurrentStreamingCameraIds() {
            Set<Set<String>> concurrentStreamingCameraIds = null;
            synchronized (mLock) {
                // Try to make sure we have an up-to-date list of concurrent camera devices.
                connectCameraServiceLocked();
                concurrentStreamingCameraIds = extractConcurrentCameraIdListLocked();
            }
            // TODO: Some sort of sorting  ?
            return concurrentStreamingCameraIds;
        }

        public boolean isConcurrentSessionConfigurationSupported(
                @NonNull Map<String, SessionConfiguration> cameraIdsAndSessionConfigurations)
                throws CameraAccessException {

            if (cameraIdsAndSessionConfigurations == null) {
                throw new IllegalArgumentException("cameraIdsAndSessionConfigurations was null");
            }

            int size = cameraIdsAndSessionConfigurations.size();
            if (size == 0) {
                throw new IllegalArgumentException("camera id and session combination is empty");
            }

            synchronized (mLock) {
                // Go through all the elements and check if the camera ids are valid at least /
                // belong to one of the combinations returned by getConcurrentStreamingCameraIds()
                boolean subsetFound = false;
                for (Set<String> combination : mConcurrentCameraIdCombinations) {
                    if (combination.containsAll(cameraIdsAndSessionConfigurations.keySet())) {
                        subsetFound = true;
                    }
                }
                if (!subsetFound) {
                    throw new IllegalArgumentException(
                            "The set of camera ids provided is not a subset of"
                            + "getConcurrentStreamingCameraIds");
                }
                CameraIdAndSessionConfiguration [] cameraIdsAndConfigs =
                        new CameraIdAndSessionConfiguration[size];
                int i = 0;
                for (Map.Entry<String, SessionConfiguration> pair :
                        cameraIdsAndSessionConfigurations.entrySet()) {
                    cameraIdsAndConfigs[i] =
                            new CameraIdAndSessionConfiguration(pair.getKey(), pair.getValue());
                    i++;
                }
                try {
                    return mCameraService.isConcurrentSessionConfigurationSupported(
                            cameraIdsAndConfigs);
                } catch (ServiceSpecificException e) {
                   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);
                }
            }

            return false;
        }

      /**
        * Helper function to find out if a camera id is in the set of combinations returned by
        * getConcurrentStreamingCameraIds()
        * @param cameraId the unique identifier of the camera device to query
        * @return Whether the camera device was found in the set of combinations returned by
        *         getConcurrentStreamingCameraIds
        */
        public boolean cameraIdHasConcurrentStreamsLocked(String cameraId) {
            if (!mDeviceStatus.containsKey(cameraId)) {
                Log.e(TAG, "cameraIdHasConcurrentStreamsLocked called on non existing camera id");
                return false;
            }
            for (Set<String> comb : mConcurrentCameraIdCombinations) {
                if (comb.contains(cameraId)) {
                    return true;
                }
            }
            return false;
        }

        public void setTorchMode(String cameraId, boolean enabled) throws CameraAccessException {
            synchronized(mLock) {

@@ -1698,6 +1890,8 @@ public final class CameraManager {
                            cameraId);
                }

                mConcurrentCameraIdCombinations.clear();

                scheduleCameraServiceReconnectionLocked();
            }
        }
Loading