content: /*
   * Copyright (C) 2014 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.legacy;
  
  import android.hardware.Camera;
  import android.hardware.Camera.Parameters;
  import android.hardware.camera2.impl.CameraMetadataNative;
  import android.hardware.camera2.CaptureRequest;
  import android.hardware.camera2.CaptureResult;
  import android.hardware.camera2.utils.ParamsUtils;
  import android.util.Log;
  
  import java.util.Objects;
  
  import static android.hardware.camera2.CaptureRequest.*;
  import static com.android.internal.util.Preconditions.*;
  
  /**
   * Map capture request data into legacy focus state transitions.
   *
   * <p>This object will asynchronously process auto-focus changes, so no interaction
   * with it is necessary beyond reading the current state and updating with the latest trigger.</p>
   */
  @SuppressWarnings("deprecation")
  public class LegacyFocusStateMapper {
      private static String TAG = "LegacyFocusStateMapper";
      private static final boolean DEBUG = false;
  
      private final Camera mCamera;
  
      private int mAfStatePrevious = CONTROL_AF_STATE_INACTIVE;
      private String mAfModePrevious = null;
  
      /** Guard mAfRun and mAfState */
      private final Object mLock = new Object();
      /** Guard access with mLock */
      private int mAfRun = 0;
      /** Guard access with mLock */
      private int mAfState = CONTROL_AF_STATE_INACTIVE;
  
      /**
       * Instantiate a new focus state mapper.
       *
       * @param camera a non-{@code null} camera1 device
       *
       * @throws NullPointerException if any of the args were {@code null}
       */
      public LegacyFocusStateMapper(Camera camera) {
          mCamera = checkNotNull(camera, "camera must not be null");
      }
  
      /**
       * Process the AF triggers from the request as a camera1 autofocus routine.
       *
       * <p>This method should be called after the parameters are {@link LegacyRequestMapper mapped}
       * with the request.</p>
       *
       * <p>Callbacks are processed in the background, and the next call to {@link #mapResultTriggers}
       * will have the latest AF state as reflected by the camera1 callbacks.</p>
       *
       * <p>None of the arguments will be mutated.</p>
       *
       * @param captureRequest a non-{@code null} request
       * @param parameters a non-{@code null} parameters corresponding to this request (read-only)
       */
      public void processRequestTriggers(CaptureRequest captureRequest,
              Camera.Parameters parameters) {
          checkNotNull(captureRequest, "captureRequest must not be null");
  
          /*
           * control.afTrigger
           */
          int afTrigger = ParamsUtils.getOrDefault(captureRequest, CONTROL_AF_TRIGGER,
                  CONTROL_AF_TRIGGER_IDLE);
  
          final String afMode = parameters.getFocusMode();
  
          if (!Objects.equals(mAfModePrevious, afMode)) {
              if (DEBUG) {
                  Log.v(TAG, "processRequestTriggers - AF mode switched from " + mAfModePrevious +
                          " to " + afMode);
              }
  
              // Switching modes always goes back to INACTIVE; ignore callbacks from previous modes
  
              synchronized (mLock) {
                  ++mAfRun;
                  mAfState = CONTROL_AF_STATE_INACTIVE;
              }
              mCamera.cancelAutoFocus();
          }
  
          mAfModePrevious = afMode;
  
          // Passive AF Scanning
          {
              final int currentAfRun;
  
              synchronized (mLock) {
                  currentAfRun = mAfRun;
              }
  
              Camera.AutoFocusMoveCallback afMoveCallback = new Camera.AutoFocusMoveCallback() {
                  @Override
                  public void onAutoFocusMoving(boolean start, Camera camera) {
                      synchronized (mLock) {
                          int latestAfRun = mAfRun;
  
                          if (DEBUG) {
                              Log.v(TAG,
                                      "onAutoFocusMoving - start " + start + " latest AF run " +
                                              latestAfRun + ", last AF run " + currentAfRun
                              );
                          }
  
                          if (currentAfRun != latestAfRun) {
                              Log.d(TAG,
                                      "onAutoFocusMoving - ignoring move callbacks from old af run"
                                              + currentAfRun
                              );
                              return;
                          }
  
                          int newAfState = start ?
                                  CONTROL_AF_STATE_PASSIVE_SCAN :
                                  CONTROL_AF_STATE_PASSIVE_FOCUSED;
                          // We never send CONTROL_AF_STATE_PASSIVE_UNFOCUSED
  
                          switch (afMode) {
                              case Parameters.FOCUS_MODE_CONTINUOUS_PICTURE:
                              case Parameters.FOCUS_MODE_CONTINUOUS_VIDEO:
                                  break;
                              // This callback should never be sent in any other AF mode
                              default:
                                  Log.w(TAG, "onAutoFocus - got unexpected onAutoFocus in mode "
                                          + afMode);
  
                          }
  
                          mAfState = newAfState;
                      }
                  }
              };
  
              // Only set move callback if we can call autofocus.
              switch (afMode) {
                  case Parameters.FOCUS_MODE_AUTO:
                  case Parameters.FOCUS_MODE_MACRO:
                  case Parameters.FOCUS_MODE_CONTINUOUS_PICTURE:
                  case Parameters.FOCUS_MODE_CONTINUOUS_VIDEO:
                      mCamera.setAutoFocusMoveCallback(afMoveCallback);
              }
          }
  
  
          // AF Locking
          switch (afTrigger) {
              case CONTROL_AF_TRIGGER_START:
  
                  int afStateAfterStart;
                  switch (afMode) {
                      case Parameters.FOCUS_MODE_AUTO:
                      case Parameters.FOCUS_MODE_MACRO:
                          afStateAfterStart = CONTROL_AF_STATE_ACTIVE_SCAN;
                          break;
                      case Parameters.FOCUS_MODE_CONTINUOUS_PICTURE:
                      case Parameters.FOCUS_MODE_CONTINUOUS_VIDEO:
                          afStateAfterStart = CONTROL_AF_STATE_PASSIVE_SCAN;
                          break;
                      default:
                          // EDOF, INFINITY
                          afStateAfterStart = CONTROL_AF_STATE_INACTIVE;
                  }
  
                  final int currentAfRun;
                  synchronized (mLock) {
                      currentAfRun = ++mAfRun;
                      mAfState = afStateAfterStart;
                  }
  
                  if (DEBUG) {
                      Log.v(TAG, "processRequestTriggers - got AF_TRIGGER_START, " +
                              "new AF run is " + currentAfRun);
                  }
  
                  // Avoid calling autofocus unless we are in a state that supports calling this.
                  if (afStateAfterStart == CONTROL_AF_STATE_INACTIVE) {
                      break;
                  }
  
                  mCamera.autoFocus(new Camera.AutoFocusCallback() {
                      @Override
                      public void onAutoFocus(boolean success, Camera camera) {
                          synchronized (mLock) {
                              int latestAfRun = mAfRun;
  
                              if (DEBUG) {
                                  Log.v(TAG, "onAutoFocus - success " + success + " latest AF run " +
                                          latestAfRun + ", last AF run " + currentAfRun);
                              }
  
                              // Ignore old auto-focus results, since another trigger was requested
                              if (latestAfRun != currentAfRun) {
                                  Log.d(TAG, String.format("onAutoFocus - ignoring AF callback " +
                                          "(old run %d, new run %d)", currentAfRun, latestAfRun));
  
                                  return;
                              }
  
                              int newAfState = success ?
                                      CONTROL_AF_STATE_FOCUSED_LOCKED :
                                      CONTROL_AF_STATE_NOT_FOCUSED_LOCKED;
  
                              switch (afMode) {
                                  case Parameters.FOCUS_MODE_AUTO:
                                  case Parameters.FOCUS_MODE_CONTINUOUS_PICTURE:
                                  case Parameters.FOCUS_MODE_CONTINUOUS_VIDEO:
                                  case Parameters.FOCUS_MODE_MACRO:
                                      break;
                                  // This callback should never be sent in any other AF mode
                                  default:
                                      Log.w(TAG, "onAutoFocus - got unexpected onAutoFocus in mode "
                                              + afMode);
  
                              }
  
                              mAfState = newAfState;
                          }
                      }
                  });
  
                  break;
              case CONTROL_AF_TRIGGER_CANCEL:
                  synchronized (mLock) {
                      int updatedAfRun;
  
                      synchronized (mLock) {
                          updatedAfRun = ++mAfRun;
                          mAfState = CONTROL_AF_STATE_INACTIVE;
                      }
  
                      mCamera.cancelAutoFocus();
  
                      if (DEBUG) {
                          Log.v(TAG, "processRequestTriggers - got AF_TRIGGER_CANCEL, " +
                                  "new AF run is " + updatedAfRun);
                      }
                  }
  
                  break;
              case CONTROL_AF_TRIGGER_IDLE:
                  // No action necessary. The callbacks will handle transitions.
                  break;
              default:
                  Log.w(TAG, "processRequestTriggers - ignoring unknown control.afTrigger = "
                          + afTrigger);
          }
      }
  
      /**
       * Update the {@code result} camera metadata map with the new value for the
       * {@code control.afState}.
       *
       * <p>AF callbacks are processed in the background, and each call to {@link #mapResultTriggers}
       * will have the latest AF state as reflected by the camera1 callbacks.</p>
       *
       * @param result a non-{@code null} result
       */
      public void mapResultTriggers(CameraMetadataNative result) {
          checkNotNull(result, "result must not be null");
  
          int newAfState;
          synchronized (mLock) {
              newAfState = mAfState;
          }
  
          if (DEBUG && newAfState != mAfStatePrevious) {
              Log.v(TAG, String.format("mapResultTriggers - afState changed from %s to %s",
                      afStateToString(mAfStatePrevious), afStateToString(newAfState)));
          }
  
          result.set(CaptureResult.CONTROL_AF_STATE, newAfState);
  
          mAfStatePrevious = newAfState;
      }
  
      private static String afStateToString(int afState) {
          switch (afState) {
              case CONTROL_AF_STATE_ACTIVE_SCAN:
                  return "ACTIVE_SCAN";
              case CONTROL_AF_STATE_FOCUSED_LOCKED:
                  return "FOCUSED_LOCKED";
              case CONTROL_AF_STATE_INACTIVE:
                  return "INACTIVE";
              case CONTROL_AF_STATE_NOT_FOCUSED_LOCKED:
                  return "NOT_FOCUSED_LOCKED";
              case CONTROL_AF_STATE_PASSIVE_FOCUSED:
                  return "PASSIVE_FOCUSED";
              case CONTROL_AF_STATE_PASSIVE_SCAN:
                  return "PASSIVE_SCAN";
              case CONTROL_AF_STATE_PASSIVE_UNFOCUSED:
                  return "PASSIVE_UNFOCUSED";
              default :
                  return "UNKNOWN(" + afState + ")";
          }
      }
  }
  