Merge "Camera2: Fix session close callback" into mnc-dev
diff --git a/core/java/android/hardware/camera2/impl/CameraCaptureSessionImpl.java b/core/java/android/hardware/camera2/impl/CameraCaptureSessionImpl.java
index 9046e81..7f4a76c 100644
--- a/core/java/android/hardware/camera2/impl/CameraCaptureSessionImpl.java
+++ b/core/java/android/hardware/camera2/impl/CameraCaptureSessionImpl.java
@@ -67,8 +67,6 @@
     private final TaskSingleDrainer mIdleDrainer;
     /** Drain state transitions from BUSY -> IDLE */
     private final TaskSingleDrainer mAbortDrainer;
-    /** Drain the UNCONFIGURED state transition */
-    private final TaskSingleDrainer mUnconfigureDrainer;
 
     /** This session is closed; all further calls will throw ISE */
     private boolean mClosed = false;
@@ -121,8 +119,6 @@
                 /*name*/"idle");
         mAbortDrainer = new TaskSingleDrainer(mDeviceHandler, new AbortDrainListener(),
                 /*name*/"abort");
-        mUnconfigureDrainer = new TaskSingleDrainer(mDeviceHandler, new UnconfigureDrainListener(),
-                /*name*/"unconf");
 
         // CameraDevice should call configureOutputs and have it finish before constructing us
 
@@ -572,26 +568,6 @@
             @Override
             public void onUnconfigured(CameraDevice camera) {
                 if (VERBOSE) Log.v(TAG, mIdString + "onUnconfigured");
-                synchronized (session) {
-                    // Ignore #onUnconfigured before #close is called.
-                    //
-                    // Normally, this is reached when this session is closed and no immediate other
-                    // activity happens for the camera, in which case the camera is configured to
-                    // null streams by this session and the UnconfigureDrainer task is started.
-                    // However, we can also end up here if
-                    //
-                    // 1) Session is closed
-                    // 2) New session is created before this session finishes closing, setting
-                    //    mSkipUnconfigure and therefore this session does not configure null or
-                    //    start the UnconfigureDrainer task.
-                    // 3) And then the new session fails to be created, so onUnconfigured fires
-                    //    _anyway_.
-                    // In this second case, need to not finish a task that was never started, so
-                    // guard with mSkipUnconfigure
-                    if (mClosed && mConfigureSuccess && !mSkipUnconfigure) {
-                        mUnconfigureDrainer.taskFinished();
-                    }
-                }
             }
 
             @Override
@@ -656,6 +632,19 @@
              * then the drain immediately finishes.
              */
             if (VERBOSE) Log.v(TAG, mIdString + "onSequenceDrained");
+
+
+            // Fire session close as soon as all sequences are complete.
+            // We may still need to unconfigure the device, but a new session might be created
+            // past this point, and notifications would then stop to this instance.
+            mStateCallback.onClosed(CameraCaptureSessionImpl.this);
+
+            // Fast path: A new capture session has replaced this one; don't wait for abort/idle
+            // as we won't get state updates any more anyway.
+            if (mSkipUnconfigure) {
+                return;
+            }
+
             mAbortDrainer.beginDrain();
         }
     }
@@ -673,6 +662,12 @@
                  *
                  * If the camera is already "IDLE", then the drain immediately finishes.
                  */
+
+                // Fast path: A new capture session has replaced this one; don't wait for idle
+                // as we won't get state updates any more anyway.
+                if (mSkipUnconfigure) {
+                    return;
+                }
                 mIdleDrainer.beginDrain();
             }
         }
@@ -691,7 +686,7 @@
                  * The device is now IDLE, and has settled. It will not transition to
                  * ACTIVE or BUSY again by itself.
                  *
-                 * It's now safe to unconfigure the outputs and after it's done invoke #onClosed.
+                 * It's now safe to unconfigure the outputs.
                  *
                  * This operation is idempotent; a session will not be closed twice.
                  */
@@ -699,45 +694,31 @@
                         Log.v(TAG, mIdString + "Session drain complete, skip unconfigure: " +
                                 mSkipUnconfigure);
 
-                    // Fast path: A new capture session has replaced this one; don't unconfigure.
+                    // Fast path: A new capture session has replaced this one; don't wait for idle
+                    // as we won't get state updates any more anyway.
                     if (mSkipUnconfigure) {
-                        mStateCallback.onClosed(CameraCaptureSessionImpl.this);
                         return;
                     }
 
-                    // Slow path: #close was called explicitly on this session; unconfigure first
-                    mUnconfigureDrainer.taskStarted();
-
+                    // Final slow path: unconfigure the camera, no session has replaced us and
+                    // everything is idle.
                     try {
                         // begin transition to unconfigured
                         mDeviceImpl.configureStreamsChecked(null, null);
                     } catch (CameraAccessException e) {
                         // OK: do not throw checked exceptions.
-                        Log.e(TAG, mIdString + "Exception while configuring outputs: ", e);
+                        Log.e(TAG, mIdString + "Exception while unconfiguring outputs: ", e);
 
                         // TODO: call onError instead of onClosed if this happens
                     } catch (IllegalStateException e) {
-                        // Camera is already closed, so go straight to the close callback
+                        // Camera is already closed, so nothing left to do
                         if (VERBOSE) Log.v(TAG, mIdString +
                                 "Camera was already closed or busy, skipping unconfigure");
-                        mUnconfigureDrainer.taskFinished();
                     }
 
-                    mUnconfigureDrainer.beginDrain();
                 }
             }
         }
     }
 
-    private class UnconfigureDrainListener implements TaskDrainer.DrainListener {
-        @Override
-
-        public void onDrained() {
-            if (VERBOSE) Log.v(TAG, mIdString + "onUnconfigureDrained");
-            synchronized (CameraCaptureSessionImpl.this) {
-                // The device has finished unconfiguring. It's now fully closed.
-                mStateCallback.onClosed(CameraCaptureSessionImpl.this);
-            }
-        }
-    }
 }