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

Commit 0dcd217e authored by Emilian Peev's avatar Emilian Peev Committed by Automerger Merge Worker
Browse files

Merge "Camera: Keep sensor orientation consistent with device fold state" into...

Merge "Camera: Keep sensor orientation consistent with device fold state" into sc-v2-dev am: 01d8df41

Original change: https://googleplex-android-review.googlesource.com/c/platform/frameworks/base/+/15908724

Change-Id: Ied80bf349069fb87229a1d36b59efd0afff95661
parents 29509b34 01d8df41
Loading
Loading
Loading
Loading
+7 −0
Original line number Original line Diff line number Diff line
@@ -17966,6 +17966,7 @@ package android.hardware.camera2 {
    field @NonNull public static final android.hardware.camera2.CameraCharacteristics.Key<int[]> EDGE_AVAILABLE_EDGE_MODES;
    field @NonNull public static final android.hardware.camera2.CameraCharacteristics.Key<int[]> EDGE_AVAILABLE_EDGE_MODES;
    field @NonNull public static final android.hardware.camera2.CameraCharacteristics.Key<java.lang.Boolean> FLASH_INFO_AVAILABLE;
    field @NonNull public static final android.hardware.camera2.CameraCharacteristics.Key<java.lang.Boolean> FLASH_INFO_AVAILABLE;
    field @NonNull public static final android.hardware.camera2.CameraCharacteristics.Key<int[]> HOT_PIXEL_AVAILABLE_HOT_PIXEL_MODES;
    field @NonNull public static final android.hardware.camera2.CameraCharacteristics.Key<int[]> HOT_PIXEL_AVAILABLE_HOT_PIXEL_MODES;
    field @NonNull public static final android.hardware.camera2.CameraCharacteristics.Key<android.hardware.camera2.params.DeviceStateOrientationMap> INFO_DEVICE_STATE_ORIENTATION_MAP;
    field @NonNull public static final android.hardware.camera2.CameraCharacteristics.Key<java.lang.Integer> INFO_SUPPORTED_HARDWARE_LEVEL;
    field @NonNull public static final android.hardware.camera2.CameraCharacteristics.Key<java.lang.Integer> INFO_SUPPORTED_HARDWARE_LEVEL;
    field @NonNull public static final android.hardware.camera2.CameraCharacteristics.Key<java.lang.String> INFO_VERSION;
    field @NonNull public static final android.hardware.camera2.CameraCharacteristics.Key<java.lang.String> INFO_VERSION;
    field @NonNull public static final android.hardware.camera2.CameraCharacteristics.Key<android.util.Size[]> JPEG_AVAILABLE_THUMBNAIL_SIZES;
    field @NonNull public static final android.hardware.camera2.CameraCharacteristics.Key<android.util.Size[]> JPEG_AVAILABLE_THUMBNAIL_SIZES;
@@ -18660,6 +18661,12 @@ package android.hardware.camera2.params {
    method public android.util.Rational getElement(int, int);
    method public android.util.Rational getElement(int, int);
  }
  }
  public final class DeviceStateOrientationMap {
    method public int getSensorOrientation(long);
    field public static final long FOLDED = 4L; // 0x4L
    field public static final long NORMAL = 0L; // 0x0L
  }
  public final class ExtensionSessionConfiguration {
  public final class ExtensionSessionConfiguration {
    ctor public ExtensionSessionConfiguration(int, @NonNull java.util.List<android.hardware.camera2.params.OutputConfiguration>, @NonNull java.util.concurrent.Executor, @NonNull android.hardware.camera2.CameraExtensionSession.StateCallback);
    ctor public ExtensionSessionConfiguration(int, @NonNull java.util.List<android.hardware.camera2.params.OutputConfiguration>, @NonNull java.util.concurrent.Executor, @NonNull android.hardware.camera2.CameraExtensionSession.StateCallback);
    method @NonNull public java.util.concurrent.Executor getExecutor();
    method @NonNull public java.util.concurrent.Executor getExecutor();
+113 −2
Original line number Original line Diff line number Diff line
@@ -22,15 +22,18 @@ import android.compat.annotation.UnsupportedAppUsage;
import android.hardware.camera2.impl.CameraMetadataNative;
import android.hardware.camera2.impl.CameraMetadataNative;
import android.hardware.camera2.impl.PublicKey;
import android.hardware.camera2.impl.PublicKey;
import android.hardware.camera2.impl.SyntheticKey;
import android.hardware.camera2.impl.SyntheticKey;
import android.hardware.camera2.params.DeviceStateOrientationMap;
import android.hardware.camera2.params.RecommendedStreamConfigurationMap;
import android.hardware.camera2.params.RecommendedStreamConfigurationMap;
import android.hardware.camera2.params.SessionConfiguration;
import android.hardware.camera2.params.SessionConfiguration;
import android.hardware.camera2.utils.TypeReference;
import android.hardware.camera2.utils.TypeReference;
import android.os.Build;
import android.os.Build;
import android.util.Log;
import android.util.Rational;
import android.util.Rational;


import com.android.internal.annotations.GuardedBy;

import java.util.ArrayList;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.List;
import java.util.Set;
import java.util.Set;


@@ -202,8 +205,25 @@ public final class CameraCharacteristics extends CameraMetadata<CameraCharacteri
    private List<CaptureResult.Key<?>> mAvailableResultKeys;
    private List<CaptureResult.Key<?>> mAvailableResultKeys;
    private ArrayList<RecommendedStreamConfigurationMap> mRecommendedConfigurations;
    private ArrayList<RecommendedStreamConfigurationMap> mRecommendedConfigurations;


    private final Object mLock = new Object();
    @GuardedBy("mLock")
    private boolean mFoldedDeviceState;

    private final CameraManager.DeviceStateListener mFoldStateListener =
            new CameraManager.DeviceStateListener() {
                @Override
                public final void onDeviceStateChanged(boolean folded) {
                    synchronized (mLock) {
                        mFoldedDeviceState = folded;
                    }
                }};

    private static final String TAG = "CameraCharacteristics";

    /**
    /**
     * Takes ownership of the passed-in properties object
     * Takes ownership of the passed-in properties object
     *
     * @param properties Camera properties.
     * @hide
     * @hide
     */
     */
    public CameraCharacteristics(CameraMetadataNative properties) {
    public CameraCharacteristics(CameraMetadataNative properties) {
@@ -219,6 +239,41 @@ public final class CameraCharacteristics extends CameraMetadata<CameraCharacteri
        return new CameraMetadataNative(mProperties);
        return new CameraMetadataNative(mProperties);
    }
    }


    /**
     * Return the device state listener for this Camera characteristics instance
     */
    CameraManager.DeviceStateListener getDeviceStateListener() { return mFoldStateListener; }

    /**
     * Overrides the property value
     *
     * <p>Check whether a given property value needs to be overridden in some specific
     * case.</p>
     *
     * @param key The characteristics field to override.
     * @return The value of overridden property, or {@code null} if the property doesn't need an
     * override.
     */
    @Nullable
    private <T> T overrideProperty(Key<T> key) {
        if (CameraCharacteristics.SENSOR_ORIENTATION.equals(key) && (mFoldStateListener != null) &&
                (mProperties.get(CameraCharacteristics.INFO_DEVICE_STATE_ORIENTATIONS) != null)) {
            DeviceStateOrientationMap deviceStateOrientationMap =
                    mProperties.get(CameraCharacteristics.INFO_DEVICE_STATE_ORIENTATION_MAP);
            synchronized (mLock) {
                Integer ret = deviceStateOrientationMap.getSensorOrientation(mFoldedDeviceState ?
                        DeviceStateOrientationMap.FOLDED : DeviceStateOrientationMap.NORMAL);
                if (ret >= 0) {
                    return (T) ret;
                } else {
                    Log.w(TAG, "No valid device state to orientation mapping! Using default!");
                }
            }
        }

        return null;
    }

    /**
    /**
     * Get a camera characteristics field value.
     * Get a camera characteristics field value.
     *
     *
@@ -235,7 +290,8 @@ public final class CameraCharacteristics extends CameraMetadata<CameraCharacteri
     */
     */
    @Nullable
    @Nullable
    public <T> T get(Key<T> key) {
    public <T> T get(Key<T> key) {
        return mProperties.get(key);
        T propertyOverride = overrideProperty(key);
        return (propertyOverride != null) ? propertyOverride : mProperties.get(key);
    }
    }


    /**
    /**
@@ -3993,11 +4049,26 @@ public final class CameraCharacteristics extends CameraMetadata<CameraCharacteri
     * upright on the device screen in its native orientation.</p>
     * upright on the device screen in its native orientation.</p>
     * <p>Also defines the direction of rolling shutter readout, which is from top to bottom in
     * <p>Also defines the direction of rolling shutter readout, which is from top to bottom in
     * the sensor's coordinate system.</p>
     * the sensor's coordinate system.</p>
     * <p>Starting with Android API level 32, camera clients that query the orientation via
     * {@link android.hardware.camera2.CameraCharacteristics#get } on foldable devices which
     * include logical cameras can receive a value that can dynamically change depending on the
     * device/fold state.
     * Clients are advised to not cache or store the orientation value of such logical sensors.
     * In case repeated queries to CameraCharacteristics are not preferred, then clients can
     * also access the entire mapping from device state to sensor orientation in
     * {@link android.hardware.camera2.params.DeviceStateOrientationMap }.
     * Do note that a dynamically changing sensor orientation value in camera characteristics
     * will not be the best way to establish the orientation per frame. Clients that want to
     * know the sensor orientation of a particular captured frame should query the
     * {@link CaptureResult#LOGICAL_MULTI_CAMERA_ACTIVE_PHYSICAL_ID android.logicalMultiCamera.activePhysicalId} from the corresponding capture result and
     * check the respective physical camera orientation.</p>
     * <p><b>Units</b>: Degrees of clockwise rotation; always a multiple of
     * <p><b>Units</b>: Degrees of clockwise rotation; always a multiple of
     * 90</p>
     * 90</p>
     * <p><b>Range of valid values:</b><br>
     * <p><b>Range of valid values:</b><br>
     * 0, 90, 180, 270</p>
     * 0, 90, 180, 270</p>
     * <p>This key is available on all devices.</p>
     * <p>This key is available on all devices.</p>
     *
     * @see CaptureResult#LOGICAL_MULTI_CAMERA_ACTIVE_PHYSICAL_ID
     */
     */
    @PublicKey
    @PublicKey
    @NonNull
    @NonNull
@@ -4306,6 +4377,46 @@ public final class CameraCharacteristics extends CameraMetadata<CameraCharacteri
    public static final Key<String> INFO_VERSION =
    public static final Key<String> INFO_VERSION =
            new Key<String>("android.info.version", String.class);
            new Key<String>("android.info.version", String.class);


    /**
     * <p>This lists the mapping between a device folding state and
     * specific camera sensor orientation for logical cameras on a foldable device.</p>
     * <p>Logical cameras on foldable devices can support sensors with different orientation
     * values. The orientation value may need to change depending on the specific folding
     * state. Information about the mapping between the device folding state and the
     * sensor orientation can be obtained in
     * {@link android.hardware.camera2.params.DeviceStateOrientationMap }.
     * Device state orientation maps are optional and maybe present on devices that support
     * {@link CaptureRequest#SCALER_ROTATE_AND_CROP android.scaler.rotateAndCrop}.</p>
     * <p><b>Optional</b> - The value for this key may be {@code null} on some devices.</p>
     * <p><b>Limited capability</b> -
     * Present on all camera devices that report being at least {@link CameraCharacteristics#INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED HARDWARE_LEVEL_LIMITED} devices in the
     * {@link CameraCharacteristics#INFO_SUPPORTED_HARDWARE_LEVEL android.info.supportedHardwareLevel} key</p>
     *
     * @see CameraCharacteristics#INFO_SUPPORTED_HARDWARE_LEVEL
     * @see CaptureRequest#SCALER_ROTATE_AND_CROP
     */
    @PublicKey
    @NonNull
    @SyntheticKey
    public static final Key<android.hardware.camera2.params.DeviceStateOrientationMap> INFO_DEVICE_STATE_ORIENTATION_MAP =
            new Key<android.hardware.camera2.params.DeviceStateOrientationMap>("android.info.deviceStateOrientationMap", android.hardware.camera2.params.DeviceStateOrientationMap.class);

    /**
     * <p>HAL must populate the array with
     * (hardware::camera::provider::V2_5::DeviceState, sensorOrientation) pairs for each
     * supported device state bitwise combination.</p>
     * <p><b>Units</b>: (device fold state, sensor orientation) x n</p>
     * <p><b>Optional</b> - The value for this key may be {@code null} on some devices.</p>
     * <p><b>Limited capability</b> -
     * Present on all camera devices that report being at least {@link CameraCharacteristics#INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED HARDWARE_LEVEL_LIMITED} devices in the
     * {@link CameraCharacteristics#INFO_SUPPORTED_HARDWARE_LEVEL android.info.supportedHardwareLevel} key</p>
     *
     * @see CameraCharacteristics#INFO_SUPPORTED_HARDWARE_LEVEL
     * @hide
     */
    public static final Key<long[]> INFO_DEVICE_STATE_ORIENTATIONS =
            new Key<long[]>("android.info.deviceStateOrientations", long[].class);

    /**
    /**
     * <p>The maximum number of frames that can occur after a request
     * <p>The maximum number of frames that can occur after a request
     * (different than the previous) has been submitted, and before the
     * (different than the previous) has been submitted, and before the
+84 −2
Original line number Original line Diff line number Diff line
@@ -30,15 +30,19 @@ import android.hardware.ICameraServiceListener;
import android.hardware.camera2.impl.CameraDeviceImpl;
import android.hardware.camera2.impl.CameraDeviceImpl;
import android.hardware.camera2.impl.CameraInjectionSessionImpl;
import android.hardware.camera2.impl.CameraInjectionSessionImpl;
import android.hardware.camera2.impl.CameraMetadataNative;
import android.hardware.camera2.impl.CameraMetadataNative;
import android.hardware.camera2.params.DeviceStateOrientationMap;
import android.hardware.camera2.params.ExtensionSessionConfiguration;
import android.hardware.camera2.params.ExtensionSessionConfiguration;
import android.hardware.camera2.params.SessionConfiguration;
import android.hardware.camera2.params.SessionConfiguration;
import android.hardware.camera2.params.StreamConfiguration;
import android.hardware.camera2.params.StreamConfiguration;
import android.hardware.camera2.utils.CameraIdAndSessionConfiguration;
import android.hardware.camera2.utils.CameraIdAndSessionConfiguration;
import android.hardware.camera2.utils.ConcurrentCameraIdCombination;
import android.hardware.camera2.utils.ConcurrentCameraIdCombination;
import android.hardware.devicestate.DeviceStateManager;
import android.hardware.display.DisplayManager;
import android.hardware.display.DisplayManager;
import android.os.Binder;
import android.os.Binder;
import android.os.DeadObjectException;
import android.os.DeadObjectException;
import android.os.Handler;
import android.os.Handler;
import android.os.HandlerExecutor;
import android.os.HandlerThread;
import android.os.IBinder;
import android.os.IBinder;
import android.os.RemoteException;
import android.os.RemoteException;
import android.os.ServiceManager;
import android.os.ServiceManager;
@@ -50,6 +54,10 @@ import android.util.Log;
import android.util.Size;
import android.util.Size;
import android.view.Display;
import android.view.Display;


import com.android.internal.annotations.GuardedBy;
import com.android.internal.util.ArrayUtils;

import java.lang.ref.WeakReference;
import java.util.ArrayList;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Arrays;
import java.util.Comparator;
import java.util.Comparator;
@@ -97,6 +105,80 @@ public final class CameraManager {
        synchronized(mLock) {
        synchronized(mLock) {
            mContext = context;
            mContext = context;
        }
        }

        mHandlerThread = new HandlerThread(TAG);
        mHandlerThread.start();
        mHandler = new Handler(mHandlerThread.getLooper());
        mFoldStateListener = new FoldStateListener(context);
        context.getSystemService(DeviceStateManager.class)
                .registerCallback(new HandlerExecutor(mHandler), mFoldStateListener);
    }

    private HandlerThread mHandlerThread;
    private Handler mHandler;
    private FoldStateListener mFoldStateListener;
    @GuardedBy("mLock")
    private ArrayList<WeakReference<DeviceStateListener>> mDeviceStateListeners = new ArrayList<>();
    private boolean mFoldedDeviceState;

    /**
     * @hide
     */
    public interface DeviceStateListener {
        void onDeviceStateChanged(boolean folded);
    }

    private final class FoldStateListener implements DeviceStateManager.DeviceStateCallback {
        private final int[] mFoldedDeviceStates;

        public FoldStateListener(Context context) {
            mFoldedDeviceStates = context.getResources().getIntArray(
                    com.android.internal.R.array.config_foldedDeviceStates);
        }

        private void handleStateChange(int state) {
            boolean folded = ArrayUtils.contains(mFoldedDeviceStates, state);
            synchronized (mLock) {
                mFoldedDeviceState = folded;
                ArrayList<WeakReference<DeviceStateListener>> invalidListeners = new ArrayList<>();
                for (WeakReference<DeviceStateListener> listener : mDeviceStateListeners) {
                    DeviceStateListener callback = listener.get();
                    if (callback != null) {
                        callback.onDeviceStateChanged(folded);
                    } else {
                        invalidListeners.add(listener);
                    }
                }
                if (!invalidListeners.isEmpty()) {
                    mDeviceStateListeners.removeAll(invalidListeners);
                }
            }
        }

        @Override
        public final void onBaseStateChanged(int state) {
            handleStateChange(state);
        }

        @Override
        public final void onStateChanged(int state) {
            handleStateChange(state);
        }
    }

    /**
     * Register a {@link CameraCharacteristics} device state listener
     *
     * @param chars Camera characteristics that need to receive device state updates
     *
     * @hide
     */
    public void registerDeviceStateListener(@NonNull CameraCharacteristics chars) {
        synchronized (mLock) {
            DeviceStateListener listener = chars.getDeviceStateListener();
            listener.onDeviceStateChanged(mFoldedDeviceState);
            mDeviceStateListeners.add(new WeakReference<>(listener));
        }
    }
    }


    /**
    /**
@@ -504,6 +586,7 @@ public final class CameraManager {
                        "Camera service is currently unavailable", e);
                        "Camera service is currently unavailable", e);
            }
            }
        }
        }
        registerDeviceStateListener(characteristics);
        return characteristics;
        return characteristics;
    }
    }


@@ -1327,8 +1410,7 @@ public final class CameraManager {
        private ICameraService mCameraService;
        private ICameraService mCameraService;


        // Singleton, don't allow construction
        // Singleton, don't allow construction
        private CameraManagerGlobal() {
        private CameraManagerGlobal() { }
        }


        public static final boolean sCameraServiceDisabled =
        public static final boolean sCameraServiceDisabled =
                SystemProperties.getBoolean("config.disable_cameraservice", false);
                SystemProperties.getBoolean("config.disable_cameraservice", false);
+23 −1
Original line number Original line Diff line number Diff line
@@ -50,6 +50,7 @@ import android.hardware.camera2.marshal.impl.MarshalQueryableStreamConfiguration
import android.hardware.camera2.marshal.impl.MarshalQueryableStreamConfigurationDuration;
import android.hardware.camera2.marshal.impl.MarshalQueryableStreamConfigurationDuration;
import android.hardware.camera2.marshal.impl.MarshalQueryableString;
import android.hardware.camera2.marshal.impl.MarshalQueryableString;
import android.hardware.camera2.params.Capability;
import android.hardware.camera2.params.Capability;
import android.hardware.camera2.params.DeviceStateOrientationMap;
import android.hardware.camera2.params.Face;
import android.hardware.camera2.params.Face;
import android.hardware.camera2.params.HighSpeedVideoConfiguration;
import android.hardware.camera2.params.HighSpeedVideoConfiguration;
import android.hardware.camera2.params.LensShadingMap;
import android.hardware.camera2.params.LensShadingMap;
@@ -761,6 +762,15 @@ public class CameraMetadataNative implements Parcelable {
                        return (T) metadata.getLensShadingMap();
                        return (T) metadata.getLensShadingMap();
                    }
                    }
                });
                });
        sGetCommandMap.put(
                CameraCharacteristics.INFO_DEVICE_STATE_ORIENTATION_MAP.getNativeKey(),
                        new GetCommand() {
                    @Override
                    @SuppressWarnings("unchecked")
                    public <T> T getValue(CameraMetadataNative metadata, Key<T> key) {
                        return (T) metadata.getDeviceStateOrientationMap();
                    }
                });
        sGetCommandMap.put(
        sGetCommandMap.put(
                CaptureResult.STATISTICS_OIS_SAMPLES.getNativeKey(),
                CaptureResult.STATISTICS_OIS_SAMPLES.getNativeKey(),
                        new GetCommand() {
                        new GetCommand() {
@@ -994,6 +1004,18 @@ public class CameraMetadataNative implements Parcelable {
        return map;
        return map;
    }
    }


    private DeviceStateOrientationMap getDeviceStateOrientationMap() {
        long[] mapArray = getBase(CameraCharacteristics.INFO_DEVICE_STATE_ORIENTATIONS);

        // Do not warn if map is null while s is not. This is valid.
        if (mapArray == null) {
            return null;
        }

        DeviceStateOrientationMap map = new DeviceStateOrientationMap(mapArray);
        return map;
    }

    private Location getGpsLocation() {
    private Location getGpsLocation() {
        String processingMethod = get(CaptureResult.JPEG_GPS_PROCESSING_METHOD);
        String processingMethod = get(CaptureResult.JPEG_GPS_PROCESSING_METHOD);
        double[] coords = get(CaptureResult.JPEG_GPS_COORDINATES);
        double[] coords = get(CaptureResult.JPEG_GPS_COORDINATES);
+155 −0
Original line number Original line Diff line number Diff line
/*
 * Copyright (C) 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 android.hardware.camera2.params;

import android.annotation.LongDef;
import android.hardware.camera2.CameraCharacteristics;
import android.hardware.camera2.utils.HashCodeHelpers;

import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Objects;

/**
 * Immutable class that maps the device fold state to sensor orientation.
 *
 * <p>Some {@link CameraCharacteristics#REQUEST_AVAILABLE_CAPABILITIES_LOGICAL_MULTI_CAMERA logical}
 * cameras on foldables can include physical sensors with different sensor orientation
 * values. As a result, the values of the logical camera device can potentially change depending
 * on the device fold state.</p>
 *
 * <p>The device fold state to sensor orientation map will contain information about the
 * respective logical camera sensor orientation given a device state. Clients
 * can query the mapping for all possible supported folded states.
 *
 * @see CameraCharacteristics#SENSOR_ORIENTATION
 */
public final class DeviceStateOrientationMap {
    /**
     *  Needs to be kept in sync with the HIDL/AIDL DeviceState
     */

    /**
     * The device is in its normal physical configuration. This is the default if the
     * device does not support multiple different states.
     */
    public static final long NORMAL = 0;

    /**
     * The device is folded.  If not set, the device is unfolded or does not
     * support folding.
     *
     * The exact point when this status change happens during the folding
     * operation is device-specific.
     */
    public static final long FOLDED = 1 << 2;

    /** @hide */
    @Retention(RetentionPolicy.SOURCE)
    @LongDef(prefix = {"DEVICE_STATE"}, value =
            {NORMAL,
             FOLDED })
    public @interface DeviceState {};

    private final HashMap<Long, Integer> mDeviceStateOrientationMap = new HashMap<>();

    /**
     * Create a new immutable DeviceStateOrientationMap instance.
     *
     * <p>This constructor takes over the array; do not write to the array afterwards.</p>
     *
     * @param elements
     *          An array of elements describing the map
     *
     * @throws IllegalArgumentException
     *            if the {@code elements} array length is invalid, not divisible by 2 or contains
     *            invalid element values
     * @throws NullPointerException
     *            if {@code elements} is {@code null}
     *
     * @hide
     */
    public DeviceStateOrientationMap(final long[] elements) {
        mElements = Objects.requireNonNull(elements, "elements must not be null");
        if ((elements.length % 2) != 0) {
            throw new IllegalArgumentException("Device state orientation map length " +
                    elements.length + " is not even!");
        }

        for (int i = 0; i < elements.length; i += 2) {
            if ((elements[i+1] % 90) != 0) {
                throw new IllegalArgumentException("Sensor orientation not divisible by 90: " +
                        elements[i+1]);
            }

            mDeviceStateOrientationMap.put(elements[i], Math.toIntExact(elements[i + 1]));
        }
    }

    /**
     * Return the logical camera sensor orientation given a specific device fold state.
     *
     * @param deviceState Device fold state
     *
     * @return Valid {@link android.hardware.camera2.CameraCharacteristics#SENSOR_ORIENTATION} for
     *         any supported device fold state
     *
     * @throws IllegalArgumentException if the given device state is invalid
     */
    public int getSensorOrientation(@DeviceState long deviceState) {
        if (!mDeviceStateOrientationMap.containsKey(deviceState)) {
            throw new IllegalArgumentException("Invalid device state: " + deviceState);
        }

        return mDeviceStateOrientationMap.get(deviceState);
    }

    /**
     * Check if this DeviceStateOrientationMap is equal to another DeviceStateOrientationMap.
     *
     * <p>Two device state orientation maps are equal if and only if all of their elements are
     * {@link Object#equals equal}.</p>
     *
     * @return {@code true} if the objects were equal, {@code false} otherwise
     */
    @Override
    public boolean equals(final Object obj) {
        if (obj == null) {
            return false;
        }
        if (this == obj) {
            return true;
        }
        if (obj instanceof DeviceStateOrientationMap) {
            final DeviceStateOrientationMap other = (DeviceStateOrientationMap) obj;
            return Arrays.equals(mElements, other.mElements);
        }
        return false;
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public int hashCode() {
        return HashCodeHelpers.hashCodeGeneric(mElements);
    }

    private final long[] mElements;
}
Loading