("android.info.deviceStateOrientations", long[].class);
+
/**
* The maximum number of frames that can occur after a request
* (different than the previous) has been submitted, and before the
diff --git a/core/java/android/hardware/camera2/CameraManager.java b/core/java/android/hardware/camera2/CameraManager.java
index 5833b3dbef869..9bb901f8c5f75 100644
--- a/core/java/android/hardware/camera2/CameraManager.java
+++ b/core/java/android/hardware/camera2/CameraManager.java
@@ -30,15 +30,19 @@ import android.hardware.ICameraServiceListener;
import android.hardware.camera2.impl.CameraDeviceImpl;
import android.hardware.camera2.impl.CameraInjectionSessionImpl;
import android.hardware.camera2.impl.CameraMetadataNative;
+import android.hardware.camera2.params.DeviceStateOrientationMap;
import android.hardware.camera2.params.ExtensionSessionConfiguration;
import android.hardware.camera2.params.SessionConfiguration;
import android.hardware.camera2.params.StreamConfiguration;
import android.hardware.camera2.utils.CameraIdAndSessionConfiguration;
import android.hardware.camera2.utils.ConcurrentCameraIdCombination;
+import android.hardware.devicestate.DeviceStateManager;
import android.hardware.display.DisplayManager;
import android.os.Binder;
import android.os.DeadObjectException;
import android.os.Handler;
+import android.os.HandlerExecutor;
+import android.os.HandlerThread;
import android.os.IBinder;
import android.os.RemoteException;
import android.os.ServiceManager;
@@ -50,6 +54,10 @@ import android.util.Log;
import android.util.Size;
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.Arrays;
import java.util.Comparator;
@@ -97,6 +105,80 @@ public final class CameraManager {
synchronized(mLock) {
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> 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> invalidListeners = new ArrayList<>();
+ for (WeakReference 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);
}
}
+ registerDeviceStateListener(characteristics);
return characteristics;
}
@@ -1327,8 +1410,7 @@ public final class CameraManager {
private ICameraService mCameraService;
// Singleton, don't allow construction
- private CameraManagerGlobal() {
- }
+ private CameraManagerGlobal() { }
public static final boolean sCameraServiceDisabled =
SystemProperties.getBoolean("config.disable_cameraservice", false);
diff --git a/core/java/android/hardware/camera2/impl/CameraMetadataNative.java b/core/java/android/hardware/camera2/impl/CameraMetadataNative.java
index 196134b397cbf..37450222aca94 100644
--- a/core/java/android/hardware/camera2/impl/CameraMetadataNative.java
+++ b/core/java/android/hardware/camera2/impl/CameraMetadataNative.java
@@ -50,6 +50,7 @@ import android.hardware.camera2.marshal.impl.MarshalQueryableStreamConfiguration
import android.hardware.camera2.marshal.impl.MarshalQueryableStreamConfigurationDuration;
import android.hardware.camera2.marshal.impl.MarshalQueryableString;
import android.hardware.camera2.params.Capability;
+import android.hardware.camera2.params.DeviceStateOrientationMap;
import android.hardware.camera2.params.Face;
import android.hardware.camera2.params.HighSpeedVideoConfiguration;
import android.hardware.camera2.params.LensShadingMap;
@@ -754,13 +755,22 @@ public class CameraMetadataNative implements Parcelable {
});
sGetCommandMap.put(
CaptureResult.STATISTICS_LENS_SHADING_CORRECTION_MAP.getNativeKey(),
- new GetCommand() {
+ new GetCommand() {
@Override
@SuppressWarnings("unchecked")
public T getValue(CameraMetadataNative metadata, Key key) {
return (T) metadata.getLensShadingMap();
}
});
+ sGetCommandMap.put(
+ CameraCharacteristics.INFO_DEVICE_STATE_ORIENTATION_MAP.getNativeKey(),
+ new GetCommand() {
+ @Override
+ @SuppressWarnings("unchecked")
+ public T getValue(CameraMetadataNative metadata, Key key) {
+ return (T) metadata.getDeviceStateOrientationMap();
+ }
+ });
sGetCommandMap.put(
CaptureResult.STATISTICS_OIS_SAMPLES.getNativeKey(),
new GetCommand() {
@@ -994,6 +1004,18 @@ public class CameraMetadataNative implements Parcelable {
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() {
String processingMethod = get(CaptureResult.JPEG_GPS_PROCESSING_METHOD);
double[] coords = get(CaptureResult.JPEG_GPS_COORDINATES);
diff --git a/core/java/android/hardware/camera2/params/DeviceStateOrientationMap.java b/core/java/android/hardware/camera2/params/DeviceStateOrientationMap.java
new file mode 100644
index 0000000000000..3907f040759ca
--- /dev/null
+++ b/core/java/android/hardware/camera2/params/DeviceStateOrientationMap.java
@@ -0,0 +1,155 @@
+/*
+ * 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.
+ *
+ * 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.
+ *
+ * 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 mDeviceStateOrientationMap = new HashMap<>();
+
+ /**
+ * Create a new immutable DeviceStateOrientationMap instance.
+ *
+ * This constructor takes over the array; do not write to the array afterwards.
+ *
+ * @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.
+ *
+ * Two device state orientation maps are equal if and only if all of their elements are
+ * {@link Object#equals equal}.
+ *
+ * @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;
+}
diff --git a/packages/services/CameraExtensionsProxy/src/com/android/cameraextensions/CameraExtensionsProxyService.java b/packages/services/CameraExtensionsProxy/src/com/android/cameraextensions/CameraExtensionsProxyService.java
index b0893cc360b7a..ed37d7e2e6e64 100644
--- a/packages/services/CameraExtensionsProxy/src/com/android/cameraextensions/CameraExtensionsProxyService.java
+++ b/packages/services/CameraExtensionsProxy/src/com/android/cameraextensions/CameraExtensionsProxyService.java
@@ -19,6 +19,7 @@ import android.app.Service;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
+import android.graphics.Camera;
import android.graphics.GraphicBuffer;
import android.graphics.Rect;
import android.hardware.camera2.CameraAccessException;
@@ -135,6 +136,7 @@ public class CameraExtensionsProxyService extends Service {
private HashMap mCharacteristicsHashMap = new HashMap<>();
private HashMap mMetadataVendorIdMap = new HashMap<>();
+ private CameraManager mCameraManager;
private static boolean checkForAdvancedAPI() {
if (EXTENSIONS_PRESENT && EXTENSIONS_VERSION.startsWith(ADVANCED_VERSION_PREFIX)) {
@@ -460,12 +462,12 @@ public class CameraExtensionsProxyService extends Service {
// This will setup the camera vendor tag descriptor in the service process
// along with all camera characteristics.
try {
- CameraManager manager = getSystemService(CameraManager.class);
+ mCameraManager = getSystemService(CameraManager.class);
- String [] cameraIds = manager.getCameraIdListNoLazy();
+ String [] cameraIds = mCameraManager.getCameraIdListNoLazy();
if (cameraIds != null) {
for (String cameraId : cameraIds) {
- CameraCharacteristics chars = manager.getCameraCharacteristics(cameraId);
+ CameraCharacteristics chars = mCameraManager.getCameraCharacteristics(cameraId);
mCharacteristicsHashMap.put(cameraId, chars);
Object thisClass = CameraCharacteristics.Key.class;
Class> keyClass =
@@ -1174,8 +1176,9 @@ public class CameraExtensionsProxyService extends Service {
@Override
public void onInit(String cameraId, CameraMetadataNative cameraCharacteristics) {
mCameraId = cameraId;
- mPreviewExtender.onInit(cameraId, new CameraCharacteristics(cameraCharacteristics),
- CameraExtensionsProxyService.this);
+ CameraCharacteristics chars = new CameraCharacteristics(cameraCharacteristics);
+ mCameraManager.registerDeviceStateListener(chars);
+ mPreviewExtender.onInit(cameraId, chars, CameraExtensionsProxyService.this);
}
@Override
@@ -1200,13 +1203,16 @@ public class CameraExtensionsProxyService extends Service {
@Override
public void init(String cameraId, CameraMetadataNative chars) {
- mPreviewExtender.init(cameraId, new CameraCharacteristics(chars));
+ CameraCharacteristics c = new CameraCharacteristics(chars);
+ mCameraManager.registerDeviceStateListener(c);
+ mPreviewExtender.init(cameraId, c);
}
@Override
public boolean isExtensionAvailable(String cameraId, CameraMetadataNative chars) {
- return mPreviewExtender.isExtensionAvailable(cameraId,
- new CameraCharacteristics(chars));
+ CameraCharacteristics c = new CameraCharacteristics(chars);
+ mCameraManager.registerDeviceStateListener(c);
+ return mPreviewExtender.isExtensionAvailable(cameraId, c);
}
@Override
@@ -1283,8 +1289,9 @@ public class CameraExtensionsProxyService extends Service {
@Override
public void onInit(String cameraId, CameraMetadataNative cameraCharacteristics) {
- mImageExtender.onInit(cameraId, new CameraCharacteristics(cameraCharacteristics),
- CameraExtensionsProxyService.this);
+ CameraCharacteristics chars = new CameraCharacteristics(cameraCharacteristics);
+ mCameraManager.registerDeviceStateListener(chars);
+ mImageExtender.onInit(cameraId, chars, CameraExtensionsProxyService.this);
mCameraId = cameraId;
}
@@ -1310,13 +1317,16 @@ public class CameraExtensionsProxyService extends Service {
@Override
public void init(String cameraId, CameraMetadataNative chars) {
- mImageExtender.init(cameraId, new CameraCharacteristics(chars));
+ CameraCharacteristics c = new CameraCharacteristics(chars);
+ mCameraManager.registerDeviceStateListener(c);
+ mImageExtender.init(cameraId, c);
}
@Override
public boolean isExtensionAvailable(String cameraId, CameraMetadataNative chars) {
- return mImageExtender.isExtensionAvailable(cameraId,
- new CameraCharacteristics(chars));
+ CameraCharacteristics c = new CameraCharacteristics(chars);
+ mCameraManager.registerDeviceStateListener(c);
+ return mImageExtender.isExtensionAvailable(cameraId, c);
}
@Override