From 2bf82bbfef4ec45e671ebc67d53174f6ffea9858 Mon Sep 17 00:00:00 2001 From: Emilian Peev Date: Fri, 24 Sep 2021 15:55:25 -0700 Subject: [PATCH] Camera: Keep sensor orientation consistent with device fold state Allow foldable devices to advertise the mapping between device fold state and sensor orientation. Additionally return the current sensor orientation based on the specific device fold state. Bug: 201005727 Test: Manual using AOSP Camera2 application, Camera CTS Change-Id: Idc76903da22567d29cc55d991b41df7e19490aa3 --- core/api/current.txt | 7 + .../camera2/CameraCharacteristics.java | 115 ++++++++++++- .../hardware/camera2/CameraManager.java | 86 +++++++++- .../camera2/impl/CameraMetadataNative.java | 24 ++- .../params/DeviceStateOrientationMap.java | 155 ++++++++++++++++++ .../CameraExtensionsProxyService.java | 36 ++-- 6 files changed, 405 insertions(+), 18 deletions(-) create mode 100644 core/java/android/hardware/camera2/params/DeviceStateOrientationMap.java diff --git a/core/api/current.txt b/core/api/current.txt index 9a16390baf10a..62f78bd07511b 100644 --- a/core/api/current.txt +++ b/core/api/current.txt @@ -17949,6 +17949,7 @@ package android.hardware.camera2 { field @NonNull public static final android.hardware.camera2.CameraCharacteristics.Key EDGE_AVAILABLE_EDGE_MODES; field @NonNull public static final android.hardware.camera2.CameraCharacteristics.Key FLASH_INFO_AVAILABLE; field @NonNull public static final android.hardware.camera2.CameraCharacteristics.Key HOT_PIXEL_AVAILABLE_HOT_PIXEL_MODES; + field @NonNull public static final android.hardware.camera2.CameraCharacteristics.Key INFO_DEVICE_STATE_ORIENTATION_MAP; field @NonNull public static final android.hardware.camera2.CameraCharacteristics.Key INFO_SUPPORTED_HARDWARE_LEVEL; field @NonNull public static final android.hardware.camera2.CameraCharacteristics.Key INFO_VERSION; field @NonNull public static final android.hardware.camera2.CameraCharacteristics.Key JPEG_AVAILABLE_THUMBNAIL_SIZES; @@ -18643,6 +18644,12 @@ package android.hardware.camera2.params { 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 { ctor public ExtensionSessionConfiguration(int, @NonNull java.util.List, @NonNull java.util.concurrent.Executor, @NonNull android.hardware.camera2.CameraExtensionSession.StateCallback); method @NonNull public java.util.concurrent.Executor getExecutor(); diff --git a/core/java/android/hardware/camera2/CameraCharacteristics.java b/core/java/android/hardware/camera2/CameraCharacteristics.java index e0138c5db1784..ddac22cd42a35 100644 --- a/core/java/android/hardware/camera2/CameraCharacteristics.java +++ b/core/java/android/hardware/camera2/CameraCharacteristics.java @@ -22,15 +22,18 @@ import android.compat.annotation.UnsupportedAppUsage; import android.hardware.camera2.impl.CameraMetadataNative; import android.hardware.camera2.impl.PublicKey; import android.hardware.camera2.impl.SyntheticKey; +import android.hardware.camera2.params.DeviceStateOrientationMap; import android.hardware.camera2.params.RecommendedStreamConfigurationMap; import android.hardware.camera2.params.SessionConfiguration; import android.hardware.camera2.utils.TypeReference; import android.os.Build; +import android.util.Log; import android.util.Rational; +import com.android.internal.annotations.GuardedBy; + import java.util.ArrayList; import java.util.Collections; -import java.util.HashSet; import java.util.List; import java.util.Set; @@ -202,8 +205,25 @@ public final class CameraCharacteristics extends CameraMetadata> mAvailableResultKeys; private ArrayList 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 + * + * @param properties Camera properties. * @hide */ public CameraCharacteristics(CameraMetadataNative properties) { @@ -219,6 +239,41 @@ public final class CameraCharacteristics extends CameraMetadataCheck whether a given property value needs to be overridden in some specific + * case.

+ * + * @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 overrideProperty(Key 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. * @@ -235,7 +290,8 @@ public final class CameraCharacteristics extends CameraMetadata T get(Key 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 *

Also defines the direction of rolling shutter readout, which is from top to bottom in * the sensor's coordinate system.

+ *

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.

*

Units: Degrees of clockwise rotation; always a multiple of * 90

*

Range of valid values:
* 0, 90, 180, 270

*

This key is available on all devices.

+ * + * @see CaptureResult#LOGICAL_MULTI_CAMERA_ACTIVE_PHYSICAL_ID */ @PublicKey @NonNull @@ -4306,6 +4377,46 @@ public final class CameraCharacteristics extends CameraMetadata INFO_VERSION = new Key("android.info.version", String.class); + /** + *

This lists the mapping between a device folding state and + * specific camera sensor orientation for logical cameras on a foldable device.

+ *

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}.

+ *

Optional - The value for this key may be {@code null} on some devices.

+ *

Limited capability - + * 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

+ * + * @see CameraCharacteristics#INFO_SUPPORTED_HARDWARE_LEVEL + * @see CaptureRequest#SCALER_ROTATE_AND_CROP + */ + @PublicKey + @NonNull + @SyntheticKey + public static final Key INFO_DEVICE_STATE_ORIENTATION_MAP = + new Key("android.info.deviceStateOrientationMap", android.hardware.camera2.params.DeviceStateOrientationMap.class); + + /** + *

HAL must populate the array with + * (hardware::camera::provider::V2_5::DeviceState, sensorOrientation) pairs for each + * supported device state bitwise combination.

+ *

Units: (device fold state, sensor orientation) x n

+ *

Optional - The value for this key may be {@code null} on some devices.

+ *

Limited capability - + * 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

+ * + * @see CameraCharacteristics#INFO_SUPPORTED_HARDWARE_LEVEL + * @hide + */ + public static final Key INFO_DEVICE_STATE_ORIENTATIONS = + new Key("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