camera2: Add scene and effect mode for LEGACY.

Bug: 15116722

Change-Id: I0797fccb5fdd8b959622c100f25b2950cfe4e115
diff --git a/core/java/android/hardware/camera2/legacy/CaptureCollector.java b/core/java/android/hardware/camera2/legacy/CaptureCollector.java
index af58a8a..307e466 100644
--- a/core/java/android/hardware/camera2/legacy/CaptureCollector.java
+++ b/core/java/android/hardware/camera2/legacy/CaptureCollector.java
@@ -236,6 +236,11 @@
                 Log.d(TAG, "queueRequest  for request " + holder.getRequestId() +
                         " - " + mInFlight + " requests remain in flight.");
             }
+
+            if (!(h.needsJpeg || h.needsPreview)) {
+                throw new IllegalStateException("Request must target at least one output surface!");
+            }
+
             if (h.needsJpeg) {
                 // Wait for all current requests to finish before queueing jpeg.
                 while (mInFlight > 0) {
@@ -259,9 +264,6 @@
                 mInFlightPreviews++;
             }
 
-            if (!(h.needsJpeg || h.needsPreview)) {
-                throw new IllegalStateException("Request must target at least one output surface!");
-            }
 
             mInFlight++;
             return true;
diff --git a/core/java/android/hardware/camera2/legacy/LegacyMetadataMapper.java b/core/java/android/hardware/camera2/legacy/LegacyMetadataMapper.java
index 633bada..533c9a8 100644
--- a/core/java/android/hardware/camera2/legacy/LegacyMetadataMapper.java
+++ b/core/java/android/hardware/camera2/legacy/LegacyMetadataMapper.java
@@ -78,6 +78,8 @@
     private static final long APPROXIMATE_SENSOR_AREA_PX = (1 << 23); // 8 megapixels
     private static final long APPROXIMATE_JPEG_ENCODE_TIME_MS = 600; // 600 milliseconds
 
+    static final int UNKNOWN_MODE = -1;
+
     /*
      * Development hijinks: Lie about not supporting certain capabilities
      *
@@ -458,7 +460,22 @@
 
         m.set(CONTROL_MAX_REGIONS, maxRegions);
 
-        // TODO rest of control fields
+        /*
+         * android.control.availableEffects
+         */
+        List<String> effectModes = p.getSupportedColorEffects();
+        int[] supportedEffectModes = (effectModes == null) ? new int[0] :
+                ArrayUtils.convertStringListToIntArray(effectModes, sLegacyEffectMode,
+                        sEffectModes);
+        m.set(CONTROL_AVAILABLE_EFFECTS, supportedEffectModes);
+
+        /*
+         * android.control.availableSceneModes
+         */
+        List<String> sceneModes = p.getSupportedSceneModes();
+        int[] supportedSceneModes = (sceneModes == null) ? new int[0] :
+                ArrayUtils.convertStringListToIntArray(sceneModes, sLegacySceneModes, sSceneModes);
+        m.set(CONTROL_AVAILABLE_SCENE_MODES, supportedSceneModes);
     }
 
     private static void mapLens(CameraMetadataNative m, Camera.Parameters p) {
@@ -680,6 +697,106 @@
         }
     }
 
+    private final static String[] sLegacySceneModes = {
+        Parameters.SCENE_MODE_AUTO,
+        Parameters.SCENE_MODE_ACTION,
+        Parameters.SCENE_MODE_PORTRAIT,
+        Parameters.SCENE_MODE_LANDSCAPE,
+        Parameters.SCENE_MODE_NIGHT,
+        Parameters.SCENE_MODE_NIGHT_PORTRAIT,
+        Parameters.SCENE_MODE_THEATRE,
+        Parameters.SCENE_MODE_BEACH,
+        Parameters.SCENE_MODE_SNOW,
+        Parameters.SCENE_MODE_SUNSET,
+        Parameters.SCENE_MODE_STEADYPHOTO,
+        Parameters.SCENE_MODE_FIREWORKS,
+        Parameters.SCENE_MODE_SPORTS,
+        Parameters.SCENE_MODE_PARTY,
+        Parameters.SCENE_MODE_CANDLELIGHT,
+        Parameters.SCENE_MODE_BARCODE,
+    };
+
+    private final static int[] sSceneModes = {
+        CameraCharacteristics.CONTROL_SCENE_MODE_DISABLED,
+        CameraCharacteristics.CONTROL_SCENE_MODE_ACTION,
+        CameraCharacteristics.CONTROL_SCENE_MODE_PORTRAIT,
+        CameraCharacteristics.CONTROL_SCENE_MODE_LANDSCAPE,
+        CameraCharacteristics.CONTROL_SCENE_MODE_NIGHT,
+        CameraCharacteristics.CONTROL_SCENE_MODE_NIGHT_PORTRAIT,
+        CameraCharacteristics.CONTROL_SCENE_MODE_THEATRE,
+        CameraCharacteristics.CONTROL_SCENE_MODE_BEACH,
+        CameraCharacteristics.CONTROL_SCENE_MODE_SNOW,
+        CameraCharacteristics.CONTROL_SCENE_MODE_SUNSET,
+        CameraCharacteristics.CONTROL_SCENE_MODE_STEADYPHOTO,
+        CameraCharacteristics.CONTROL_SCENE_MODE_FIREWORKS,
+        CameraCharacteristics.CONTROL_SCENE_MODE_SPORTS,
+        CameraCharacteristics.CONTROL_SCENE_MODE_PARTY,
+        CameraCharacteristics.CONTROL_SCENE_MODE_CANDLELIGHT,
+        CameraCharacteristics.CONTROL_SCENE_MODE_BARCODE,
+    };
+
+    static int convertSceneModeFromLegacy(String mode) {
+        if (mode == null) {
+            return CameraCharacteristics.CONTROL_SCENE_MODE_DISABLED;
+        }
+        int index = ArrayUtils.getArrayIndex(sLegacySceneModes, mode);
+        if (index < 0) {
+            return UNKNOWN_MODE;
+        }
+        return sSceneModes[index];
+    }
+
+    static String convertSceneModeToLegacy(int mode) {
+        int index = ArrayUtils.getArrayIndex(sSceneModes, mode);
+        if (index < 0) {
+            return null;
+        }
+        return sLegacySceneModes[index];
+    }
+
+    private final static String[] sLegacyEffectMode = {
+        Parameters.EFFECT_NONE,
+        Parameters.EFFECT_MONO,
+        Parameters.EFFECT_NEGATIVE,
+        Parameters.EFFECT_SOLARIZE,
+        Parameters.EFFECT_SEPIA,
+        Parameters.EFFECT_POSTERIZE,
+        Parameters.EFFECT_WHITEBOARD,
+        Parameters.EFFECT_BLACKBOARD,
+        Parameters.EFFECT_AQUA,
+    };
+
+    private final static int[] sEffectModes = {
+        CameraCharacteristics.CONTROL_EFFECT_MODE_OFF,
+        CameraCharacteristics.CONTROL_EFFECT_MODE_MONO,
+        CameraCharacteristics.CONTROL_EFFECT_MODE_NEGATIVE,
+        CameraCharacteristics.CONTROL_EFFECT_MODE_SOLARIZE,
+        CameraCharacteristics.CONTROL_EFFECT_MODE_SEPIA,
+        CameraCharacteristics.CONTROL_EFFECT_MODE_POSTERIZE,
+        CameraCharacteristics.CONTROL_EFFECT_MODE_WHITEBOARD,
+        CameraCharacteristics.CONTROL_EFFECT_MODE_BLACKBOARD,
+        CameraCharacteristics.CONTROL_EFFECT_MODE_AQUA,
+    };
+
+    static int convertEffectModeFromLegacy(String mode) {
+        if (mode == null) {
+            return CameraCharacteristics.CONTROL_EFFECT_MODE_OFF;
+        }
+        int index = ArrayUtils.getArrayIndex(sLegacyEffectMode, mode);
+        if (index < 0) {
+            return UNKNOWN_MODE;
+        }
+        return sEffectModes[index];
+    }
+
+    static String convertEffectModeToLegacy(int mode) {
+        int index = ArrayUtils.getArrayIndex(sEffectModes, mode);
+        if (index < 0) {
+            return null;
+        }
+        return sLegacyEffectMode[index];
+    }
+
     /**
      * Convert the ae antibanding mode from api1 into api2.
      *
diff --git a/core/java/android/hardware/camera2/legacy/LegacyRequestMapper.java b/core/java/android/hardware/camera2/legacy/LegacyRequestMapper.java
index fbfc39f..28726e6 100644
--- a/core/java/android/hardware/camera2/legacy/LegacyRequestMapper.java
+++ b/core/java/android/hardware/camera2/legacy/LegacyRequestMapper.java
@@ -238,6 +238,57 @@
                                 + infinityFocusSupported + ", only 0.0f is supported");
             }
         }
+
+        // control.sceneMode, control.mode
+        {
+            // TODO: Map FACE_PRIORITY scene mode to face detection.
+
+            if (params.getSupportedSceneModes() != null) {
+                int controlMode = ParamsUtils.getOrDefault(request, CONTROL_MODE,
+                    /*defaultValue*/CONTROL_MODE_AUTO);
+                String modeToSet;
+                switch (controlMode) {
+                    case CONTROL_MODE_USE_SCENE_MODE: {
+                        int sceneMode = ParamsUtils.getOrDefault(request, CONTROL_SCENE_MODE,
+                        /*defaultValue*/CONTROL_SCENE_MODE_DISABLED);
+                        String legacySceneMode = LegacyMetadataMapper.
+                                convertSceneModeToLegacy(sceneMode);
+                        if (legacySceneMode != null) {
+                            modeToSet = legacySceneMode;
+                        } else {
+                            modeToSet = Parameters.SCENE_MODE_AUTO;
+                            Log.w(TAG, "Skipping unknown requested scene mode: " + sceneMode);
+                        }
+                        break;
+                    }
+                    case CONTROL_MODE_AUTO: {
+                        modeToSet = Parameters.SCENE_MODE_AUTO;
+                        break;
+                    }
+                    default: {
+                        Log.w(TAG, "Control mode " + controlMode +
+                                " is unsupported, defaulting to AUTO");
+                        modeToSet = Parameters.SCENE_MODE_AUTO;
+                    }
+                }
+                params.setSceneMode(modeToSet);
+            }
+        }
+
+        // control.effectMode
+        {
+            if (params.getSupportedColorEffects() != null) {
+                int effectMode = ParamsUtils.getOrDefault(request, CONTROL_EFFECT_MODE,
+                    /*defaultValue*/CONTROL_EFFECT_MODE_OFF);
+                String legacyEffectMode = LegacyMetadataMapper.convertEffectModeToLegacy(effectMode);
+                if (legacyEffectMode != null) {
+                    params.setColorEffect(legacyEffectMode);
+                } else {
+                    params.setColorEffect(Parameters.EFFECT_NONE);
+                    Log.w(TAG, "Skipping unknown requested effect mode: " + effectMode);
+                }
+            }
+        }
     }
 
     private static List<Camera.Area> convertMeteringRegionsToLegacy(
diff --git a/core/java/android/hardware/camera2/legacy/LegacyResultMapper.java b/core/java/android/hardware/camera2/legacy/LegacyResultMapper.java
index 07852b9..6da5dd0 100644
--- a/core/java/android/hardware/camera2/legacy/LegacyResultMapper.java
+++ b/core/java/android/hardware/camera2/legacy/LegacyResultMapper.java
@@ -28,6 +28,7 @@
 import android.hardware.camera2.legacy.ParameterUtils.ZoomData;
 import android.hardware.camera2.params.MeteringRectangle;
 import android.hardware.camera2.utils.ListUtils;
+import android.hardware.camera2.utils.ParamsUtils;
 import android.util.Log;
 import android.util.Size;
 
@@ -153,6 +154,51 @@
                     request.get(CaptureRequest.CONTROL_AWB_MODE));
         }
 
+
+        /*
+         * control.mode
+         */
+        {
+            int controlMode = ParamsUtils.getOrDefault(request, CaptureRequest.CONTROL_MODE,
+                    CONTROL_MODE_AUTO);
+            if (controlMode == CaptureResult.CONTROL_MODE_USE_SCENE_MODE) {
+                result.set(CONTROL_MODE, CONTROL_MODE_USE_SCENE_MODE);
+            } else {
+                result.set(CONTROL_MODE, CONTROL_MODE_AUTO);
+            }
+        }
+
+        /*
+         * control.sceneMode
+         */
+        {
+            String legacySceneMode = params.getSceneMode();
+            int mode = LegacyMetadataMapper.convertSceneModeFromLegacy(legacySceneMode);
+            if (mode != LegacyMetadataMapper.UNKNOWN_MODE) {
+                result.set(CaptureResult.CONTROL_SCENE_MODE, mode);
+            }  else {
+                Log.w(TAG, "Unknown scene mode " + legacySceneMode +
+                        " returned by camera HAL, setting to disabled.");
+                result.set(CaptureResult.CONTROL_SCENE_MODE, CONTROL_SCENE_MODE_DISABLED);
+            }
+        }
+
+
+        /*
+         * control.effectMode
+         */
+        {
+            String legacyEffectMode = params.getColorEffect();
+            int mode = LegacyMetadataMapper.convertEffectModeFromLegacy(legacyEffectMode);
+            if (mode != LegacyMetadataMapper.UNKNOWN_MODE) {
+                result.set(CaptureResult.CONTROL_EFFECT_MODE, mode);
+            } else {
+                Log.w(TAG, "Unknown effect mode " + legacyEffectMode +
+                        " returned by camera HAL, setting to off.");
+                result.set(CaptureResult.CONTROL_EFFECT_MODE, CONTROL_EFFECT_MODE_OFF);
+            }
+        }
+
         /*
          * flash
          */
diff --git a/core/java/android/hardware/camera2/legacy/SurfaceTextureRenderer.java b/core/java/android/hardware/camera2/legacy/SurfaceTextureRenderer.java
index 0687264..3f24b2c 100644
--- a/core/java/android/hardware/camera2/legacy/SurfaceTextureRenderer.java
+++ b/core/java/android/hardware/camera2/legacy/SurfaceTextureRenderer.java
@@ -329,6 +329,9 @@
         mSurfaces.clear();
         mConversionSurfaces.clear();
         mPBufferPixels = null;
+        if (mSurfaceTexture != null) {
+            mSurfaceTexture.release();
+        }
         mSurfaceTexture = null;
     }
 
diff --git a/core/java/android/hardware/camera2/utils/ArrayUtils.java b/core/java/android/hardware/camera2/utils/ArrayUtils.java
index 24c85d0..9d12ab92 100644
--- a/core/java/android/hardware/camera2/utils/ArrayUtils.java
+++ b/core/java/android/hardware/camera2/utils/ArrayUtils.java
@@ -47,6 +47,19 @@
         return -1;
     }
 
+    /** Return the index of {@code needle} in the {@code array}, or else {@code -1} */
+    public static int getArrayIndex(int[] array, int needle) {
+        if (array == null) {
+            return -1;
+        }
+        for (int i = 0; i < array.length; ++i) {
+            if (array[i] == needle) {
+                return i;
+            }
+        }
+        return -1;
+    }
+
     /**
      * Create an {@code int[]} from the {@code List<>} by using {@code convertFrom} and
      * {@code convertTo} as a one-to-one map (via the index).