From: Puneet Lall Date: Fri, 30 Jan 2015 03:09:30 +0000 (-0800) Subject: Fix bug in counting open images X-Git-Tag: android-x86-6.0-r3~370^2 X-Git-Url: http://git.osdn.net/view?a=commitdiff_plain;h=1738db31add0aea5a6a6f2c137ca6fc476a10237;p=android-x86%2Fpackages-apps-Camera2.git Fix bug in counting open images Bug: 19156045 AndroidImageReaderProxy previously would return an instance of itself via onImageAvailable. This caused a problem because it bypassed the LoggingImageReaderProxy and CloseWhenDoneImageReaderProxy decorators. This change also includes additional minor fixes for potential issues, specifically, it also synchronizes access to each android.media.Image and ImageReader because they are not thread-safe. Change-Id: I0d76c07ae37b09cdec40f7d7789b49f7397fb414 --- diff --git a/src/com/android/camera/debug/Loggers.java b/src/com/android/camera/debug/Loggers.java index 6c32ba841..9d594e385 100644 --- a/src/com/android/camera/debug/Loggers.java +++ b/src/com/android/camera/debug/Loggers.java @@ -17,6 +17,7 @@ package com.android.camera.debug; import com.android.camera.debug.Log.Tag; +import com.google.common.annotations.VisibleForTesting; import javax.annotation.ParametersAreNonnullByDefault; @@ -33,14 +34,26 @@ public class Loggers { } /** - * This creates a factory that will use the standard android - * static log methods. + * This creates a factory that will use the standard android static log + * methods. */ public static Logger.Factory tagFactory() { return TagLoggerFactory.instance(); } /** + * Creates a logger factory which always returns the given logger. + */ + public static Logger.Factory factoryFor(final Logger logger) { + return new Logger.Factory() { + @Override + public Logger create(Tag tag) { + return logger; + } + }; + } + + /** * Creates loggers that eat all input and does nothing. */ private static class NoOpLoggerFactory implements Logger.Factory { @@ -48,7 +61,9 @@ public class Loggers { private static final NoOpLoggerFactory INSTANCE = new NoOpLoggerFactory(); } - public static NoOpLoggerFactory instance() { return Singleton.INSTANCE; } + public static NoOpLoggerFactory instance() { + return Singleton.INSTANCE; + } private final NoOpLogger mNoOpLogger; @@ -63,15 +78,17 @@ public class Loggers { } /** - * Creates loggers that use tag objects to write to standard - * android log output. + * Creates loggers that use tag objects to write to standard android log + * output. */ private static class TagLoggerFactory implements Logger.Factory { private static class Singleton { private static final TagLoggerFactory INSTANCE = new TagLoggerFactory(); } - public static TagLoggerFactory instance() { return Singleton.INSTANCE; } + public static TagLoggerFactory instance() { + return Singleton.INSTANCE; + } @Override public Logger create(Tag tag) { @@ -84,39 +101,49 @@ public class Loggers { */ private static class NoOpLogger implements Logger { @Override - public void d(String msg) { } + public void d(String msg) { + } @Override - public void d(String msg, Throwable tr) { } + public void d(String msg, Throwable tr) { + } @Override - public void e(String msg) { } + public void e(String msg) { + } @Override - public void e(String msg, Throwable tr) { } + public void e(String msg, Throwable tr) { + } @Override - public void i(String msg) { } + public void i(String msg) { + } @Override - public void i(String msg, Throwable tr) { } + public void i(String msg, Throwable tr) { + } @Override - public void v(String msg) { } + public void v(String msg) { + } @Override - public void v(String msg, Throwable tr) { } + public void v(String msg, Throwable tr) { + } @Override - public void w(String msg) { } + public void w(String msg) { + } @Override - public void w(String msg, Throwable tr) { } + public void w(String msg, Throwable tr) { + } } /** - * TagLogger logger writes to the standard static log output with the - * given tag object. + * TagLogger logger writes to the standard static log output with the given + * tag object. */ private static class TagLogger implements Logger { private final Log.Tag mTag; @@ -175,4 +202,4 @@ public class Loggers { Log.w(mTag, msg, tr); } } -} \ No newline at end of file +} diff --git a/src/com/android/camera/one/v2/CloseWhenDoneImageReader.java b/src/com/android/camera/one/v2/CloseWhenDoneImageReader.java index 89e216953..efaedc68f 100644 --- a/src/com/android/camera/one/v2/CloseWhenDoneImageReader.java +++ b/src/com/android/camera/one/v2/CloseWhenDoneImageReader.java @@ -55,6 +55,8 @@ public final class CloseWhenDoneImageReader extends ForwardingImageReader implem private final Object mLock; @GuardedBy("mLock") + private boolean mClosePending; + @GuardedBy("mLock") private boolean mClosed; @GuardedBy("mLock") private int mOpenImages; @@ -69,7 +71,8 @@ public final class CloseWhenDoneImageReader extends ForwardingImageReader implem private void decrementImageCount() { synchronized (mLock) { mOpenImages--; - if (mClosed && mOpenImages == 0) { + if (mClosePending && !mClosed && mOpenImages == 0) { + mClosed = true; super.close(); } } @@ -79,7 +82,7 @@ public final class CloseWhenDoneImageReader extends ForwardingImageReader implem @Nullable public ImageProxy acquireNextImage() { synchronized (mLock) { - if (!mClosed) { + if (!mClosePending && !mClosed) { ImageProxy image = super.acquireNextImage(); if (image != null) { mOpenImages++; @@ -94,7 +97,7 @@ public final class CloseWhenDoneImageReader extends ForwardingImageReader implem @Nullable public ImageProxy acquireLatestImage() { synchronized (mLock) { - if (!mClosed) { + if (!mClosePending && !mClosed) { ImageProxy image = super.acquireLatestImage(); if (image != null) { mOpenImages++; @@ -108,8 +111,12 @@ public final class CloseWhenDoneImageReader extends ForwardingImageReader implem @Override public void close() { synchronized (mLock) { - mClosed = true; + if (mClosed || mClosePending) { + return; + } + mClosePending = true; if (mOpenImages == 0) { + mClosed = true; super.close(); } } diff --git a/src/com/android/camera/one/v2/LoggingImageReader.java b/src/com/android/camera/one/v2/LoggingImageReader.java index e806dbba3..9aeb3d6da 100644 --- a/src/com/android/camera/one/v2/LoggingImageReader.java +++ b/src/com/android/camera/one/v2/LoggingImageReader.java @@ -18,43 +18,43 @@ package com.android.camera.one.v2; import com.android.camera.debug.Log.Tag; import com.android.camera.debug.Logger; -import com.android.camera.debug.Loggers; import com.android.camera.one.v2.camera2proxy.ForwardingImageProxy; import com.android.camera.one.v2.camera2proxy.ForwardingImageReader; import com.android.camera.one.v2.camera2proxy.ImageProxy; import com.android.camera.one.v2.camera2proxy.ImageReaderProxy; +import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicInteger; import javax.annotation.Nullable; final class LoggingImageReader extends ForwardingImageReader { - private static Tag TAG = new Tag("LoggingImageReader"); - private class LoggingImageProxy extends ForwardingImageProxy { + private final AtomicBoolean mClosed; + public LoggingImageProxy(ImageProxy proxy) { super(proxy); + mClosed = new AtomicBoolean(false); } @Override public void close() { - super.close(); - decrementOpenImageCount(); + if (!mClosed.getAndSet(true)) { + super.close(); + decrementOpenImageCount(); + } } } - private final Logger mLogger; + + private final Logger mLog; private final AtomicInteger mNumOpenImages; - LoggingImageReader(ImageReaderProxy delegate, Logger.Factory logFactory) { + public LoggingImageReader(ImageReaderProxy delegate, Logger.Factory logFactory) { super(delegate); - mLogger = logFactory.create(TAG); + mLog = logFactory.create(new Tag("LoggingImageReader")); mNumOpenImages = new AtomicInteger(0); } - public static LoggingImageReader create(ImageReaderProxy imageReader) { - return new LoggingImageReader(imageReader, Loggers.tagFactory()); - } - @Override @Nullable public ImageProxy acquireNextImage() { @@ -78,20 +78,19 @@ final class LoggingImageReader extends ForwardingImageReader { @Override public void close() { - mLogger.d("Closing: " + toString()); + mLog.d("Closing: " + toString()); super.close(); } private void incrementOpenImageCount() { int numOpenImages = mNumOpenImages.incrementAndGet(); if (numOpenImages >= getMaxImages()) { - mLogger.e(String.format("Open Image Count (%d) exceeds maximum (%d)!", + mLog.e(String.format("Open Image Count (%d) exceeds maximum (%d)!", numOpenImages, getMaxImages())); } } private void decrementOpenImageCount() { int numOpenImages = mNumOpenImages.decrementAndGet(); - mLogger.v("Open Image Count = " + numOpenImages); } } diff --git a/src/com/android/camera/one/v2/SimpleOneCameraFactory.java b/src/com/android/camera/one/v2/SimpleOneCameraFactory.java index 27ae44a15..a31e34c94 100644 --- a/src/com/android/camera/one/v2/SimpleOneCameraFactory.java +++ b/src/com/android/camera/one/v2/SimpleOneCameraFactory.java @@ -27,6 +27,7 @@ import com.android.camera.async.Lifetime; import com.android.camera.async.MainThread; import com.android.camera.async.Observable; import com.android.camera.async.Updatable; +import com.android.camera.debug.Loggers; import com.android.camera.one.OneCamera; import com.android.camera.one.OneCameraCharacteristics; import com.android.camera.one.v2.camera2proxy.AndroidImageReaderProxy; @@ -84,10 +85,12 @@ public class SimpleOneCameraFactory implements OneCameraFactory { final Observable flashSetting) { Lifetime lifetime = new Lifetime(); - final ImageReaderProxy imageReader = new CloseWhenDoneImageReader( - LoggingImageReader.create( - AndroidImageReaderProxy.newInstance(pictureSize.getWidth(), - pictureSize.getHeight(), mImageFormat, mMaxImageCount))); + final ImageReaderProxy imageReader = new CloseWhenDoneImageReader(new LoggingImageReader( + AndroidImageReaderProxy.newInstance( + pictureSize.getWidth(), pictureSize.getHeight(), + mImageFormat, mMaxImageCount), + Loggers.tagFactory())); + lifetime.add(imageReader); List outputSurfaces = new ArrayList<>(); diff --git a/src/com/android/camera/one/v2/ZslOneCameraFactory.java b/src/com/android/camera/one/v2/ZslOneCameraFactory.java index d30da0237..12894fa8a 100644 --- a/src/com/android/camera/one/v2/ZslOneCameraFactory.java +++ b/src/com/android/camera/one/v2/ZslOneCameraFactory.java @@ -102,9 +102,9 @@ public class ZslOneCameraFactory implements OneCameraFactory { Lifetime lifetime = new Lifetime(); final ImageReaderProxy imageReader = new CloseWhenDoneImageReader( - LoggingImageReader.create(AndroidImageReaderProxy.newInstance( + new LoggingImageReader(AndroidImageReaderProxy.newInstance( pictureSize.getWidth(), pictureSize.getHeight(), - mImageFormat, mMaxImageCount))); + mImageFormat, mMaxImageCount), Loggers.tagFactory())); lifetime.add(imageReader); lifetime.add(device); diff --git a/src/com/android/camera/one/v2/camera2proxy/AndroidImageProxy.java b/src/com/android/camera/one/v2/camera2proxy/AndroidImageProxy.java index 805950d03..ccca98d05 100644 --- a/src/com/android/camera/one/v2/camera2proxy/AndroidImageProxy.java +++ b/src/com/android/camera/one/v2/camera2proxy/AndroidImageProxy.java @@ -25,9 +25,13 @@ import java.nio.ByteBuffer; import java.util.ArrayList; import java.util.List; +import javax.annotation.concurrent.GuardedBy; +import javax.annotation.concurrent.ThreadSafe; + /** * An {@link ImageProxy} backed by an {@link android.media.Image}. */ +@ThreadSafe public class AndroidImageProxy implements ImageProxy { /** @@ -35,6 +39,12 @@ public class AndroidImageProxy implements ImageProxy { * {@link android.media.Image.Plane}. */ public class Plane implements ImageProxy.Plane { + /** + * {@link android.media.Image} is not thread-safe, and the resulting + * {@link Image.Plane} objects are not-necessarily thread-safe either, + * so all interaction must be guarded by {@link #mLock}. + */ + @GuardedBy("mLock") private final Image.Plane mPlane; public Plane(Image.Plane imagePlane) { @@ -46,7 +56,9 @@ public class AndroidImageProxy implements ImageProxy { */ @Override public int getRowStride() { - return mPlane.getRowStride(); + synchronized (mLock) { + return mPlane.getRowStride(); + } } /** @@ -54,7 +66,9 @@ public class AndroidImageProxy implements ImageProxy { */ @Override public int getPixelStride() { - return mPlane.getPixelStride(); + synchronized (mLock) { + return mPlane.getPixelStride(); + } } /** @@ -62,14 +76,23 @@ public class AndroidImageProxy implements ImageProxy { */ @Override public ByteBuffer getBuffer() { - return mPlane.getBuffer(); + synchronized (mLock) { + return mPlane.getBuffer(); + } } } + private final Object mLock; + /** + * {@link android.media.Image} is not thread-safe, so all interactions must + * be guarded by {@link #mLock}. + */ + @GuardedBy("mLock") private final android.media.Image mImage; public AndroidImageProxy(android.media.Image image) { + mLock = new Object(); mImage = image; } @@ -78,7 +101,9 @@ public class AndroidImageProxy implements ImageProxy { */ @Override public Rect getCropRect() { - return mImage.getCropRect(); + synchronized (mLock) { + return mImage.getCropRect(); + } } /** @@ -86,7 +111,9 @@ public class AndroidImageProxy implements ImageProxy { */ @Override public void setCropRect(Rect cropRect) { - mImage.setCropRect(cropRect); + synchronized (mLock) { + mImage.setCropRect(cropRect); + } } /** @@ -94,7 +121,9 @@ public class AndroidImageProxy implements ImageProxy { */ @Override public int getFormat() { - return mImage.getFormat(); + synchronized (mLock) { + return mImage.getFormat(); + } } /** @@ -102,7 +131,9 @@ public class AndroidImageProxy implements ImageProxy { */ @Override public int getHeight() { - return mImage.getHeight(); + synchronized (mLock) { + return mImage.getHeight(); + } } /** @@ -117,11 +148,16 @@ public class AndroidImageProxy implements ImageProxy { * object needs to be. So, just consider the performance when using this * function wrapper. *

+ * * @see {@link android.media.Image#getPlanes} */ @Override public List getPlanes() { - Image.Plane[] planes = mImage.getPlanes(); + Image.Plane[] planes; + + synchronized (mLock) { + planes = mImage.getPlanes(); + } List wrappedPlanes = new ArrayList<>(planes.length); @@ -136,7 +172,9 @@ public class AndroidImageProxy implements ImageProxy { */ @Override public long getTimestamp() { - return mImage.getTimestamp(); + synchronized (mLock) { + return mImage.getTimestamp(); + } } /** @@ -144,7 +182,9 @@ public class AndroidImageProxy implements ImageProxy { */ @Override public int getWidth() { - return mImage.getWidth(); + synchronized (mLock) { + return mImage.getWidth(); + } } /** @@ -152,13 +192,19 @@ public class AndroidImageProxy implements ImageProxy { */ @Override public void close() { - mImage.close(); + synchronized (mLock) { + mImage.close(); + } } @Override public String toString() { - return Objects.toStringHelper(mImage) - .add("timestamp", getTimestamp()) + Objects.ToStringHelper tsh; + + synchronized (mImage) { + tsh = Objects.toStringHelper(mImage); + } + return tsh.add("timestamp", getTimestamp()) .toString(); } } diff --git a/src/com/android/camera/one/v2/camera2proxy/AndroidImageReaderProxy.java b/src/com/android/camera/one/v2/camera2proxy/AndroidImageReaderProxy.java index 8877cccae..8a59c8380 100644 --- a/src/com/android/camera/one/v2/camera2proxy/AndroidImageReaderProxy.java +++ b/src/com/android/camera/one/v2/camera2proxy/AndroidImageReaderProxy.java @@ -17,6 +17,7 @@ package com.android.camera.one.v2.camera2proxy; import android.graphics.ImageFormat; +import android.media.Image; import android.os.Handler; import android.view.Surface; @@ -24,14 +25,18 @@ import com.google.common.base.Objects; import javax.annotation.Nonnull; import javax.annotation.Nullable; +import javax.annotation.concurrent.GuardedBy; /** * A replacement for {@link android.media.ImageReader}. */ public final class AndroidImageReaderProxy implements ImageReaderProxy { + private final Object mLock; + @GuardedBy("mLock") private final android.media.ImageReader mDelegate; public AndroidImageReaderProxy(android.media.ImageReader delegate) { + mLock = new Object(); mDelegate = delegate; } @@ -71,64 +76,95 @@ public final class AndroidImageReaderProxy implements ImageReaderProxy { @Override public int getWidth() { - return mDelegate.getWidth(); + synchronized (mLock) { + return mDelegate.getWidth(); + } } @Override public int getHeight() { - return mDelegate.getHeight(); + synchronized (mLock) { + return mDelegate.getHeight(); + } } @Override public int getImageFormat() { - return mDelegate.getImageFormat(); + synchronized (mLock) { + return mDelegate.getImageFormat(); + } } @Override public int getMaxImages() { - return mDelegate.getMaxImages(); + synchronized (mLock) { + return mDelegate.getMaxImages(); + } } @Override @Nonnull public Surface getSurface() { - return mDelegate.getSurface(); + synchronized (mLock) { + return mDelegate.getSurface(); + } } @Override @Nullable public ImageProxy acquireLatestImage() { - return new AndroidImageProxy(mDelegate.acquireLatestImage()); + synchronized (mLock) { + Image image = mDelegate.acquireLatestImage(); + if (image == null) { + return null; + } else { + return new AndroidImageProxy(image); + } + } } @Override @Nullable public ImageProxy acquireNextImage() { - return new AndroidImageProxy(mDelegate.acquireNextImage()); + synchronized (mLock) { + Image image = mDelegate.acquireNextImage(); + if (image == null) { + return null; + } else { + return new AndroidImageProxy(image); + } + } } @Override public void setOnImageAvailableListener(@Nonnull final ImageReaderProxy.OnImageAvailableListener listener, Handler handler) { - mDelegate.setOnImageAvailableListener( - new android.media.ImageReader.OnImageAvailableListener() { - @Override - public void onImageAvailable(android.media.ImageReader imageReader) { - listener.onImageAvailable(AndroidImageReaderProxy.this); - } - }, handler); + synchronized (mLock) { + mDelegate.setOnImageAvailableListener( + new android.media.ImageReader.OnImageAvailableListener() { + @Override + public void onImageAvailable(android.media.ImageReader imageReader) { + listener.onImageAvailable(); + } + }, handler); + } } @Override public void close() { - mDelegate.close(); + synchronized (mLock) { + mDelegate.close(); + } } @Override public String toString() { - return Objects.toStringHelper(mDelegate) - .add("width", getWidth()) + Objects.ToStringHelper tsh; + synchronized (mLock) { + tsh = Objects.toStringHelper(mDelegate); + } + return tsh.add("width", getWidth()) .add("height", getHeight()) .add("format", imageFormatToString(getImageFormat())) .toString(); diff --git a/src/com/android/camera/one/v2/camera2proxy/ForwardingImageProxy.java b/src/com/android/camera/one/v2/camera2proxy/ForwardingImageProxy.java index 675b05ff3..16d9ad009 100644 --- a/src/com/android/camera/one/v2/camera2proxy/ForwardingImageProxy.java +++ b/src/com/android/camera/one/v2/camera2proxy/ForwardingImageProxy.java @@ -20,9 +20,12 @@ import android.graphics.Rect; import java.util.List; +import javax.annotation.concurrent.ThreadSafe; + /** * Forwards all {@link ImageProxy} methods. */ +@ThreadSafe public abstract class ForwardingImageProxy implements ImageProxy { private final ImageProxy mImpl; diff --git a/src/com/android/camera/one/v2/camera2proxy/ImageProxy.java b/src/com/android/camera/one/v2/camera2proxy/ImageProxy.java index 27470c3c2..694813cce 100644 --- a/src/com/android/camera/one/v2/camera2proxy/ImageProxy.java +++ b/src/com/android/camera/one/v2/camera2proxy/ImageProxy.java @@ -17,20 +17,25 @@ package com.android.camera.one.v2.camera2proxy; import android.graphics.Rect; -import android.media.Image; import com.android.camera.async.SafeCloseable; import java.nio.ByteBuffer; import java.util.List; +import javax.annotation.concurrent.ThreadSafe; + /** * Wraps {@link android.media.Image} with a mockable interface. + *

+ * Implementations must be thread-safe. */ +@ThreadSafe public interface ImageProxy extends SafeCloseable { /** - * Wraps the inner class {@link android.media.Image} with a mockable interface. + * Wraps the inner class {@link android.media.Image} with a mockable + * interface. */ public interface Plane { diff --git a/src/com/android/camera/one/v2/camera2proxy/ImageReaderProxy.java b/src/com/android/camera/one/v2/camera2proxy/ImageReaderProxy.java index d7f36cd1a..36aa7dacf 100644 --- a/src/com/android/camera/one/v2/camera2proxy/ImageReaderProxy.java +++ b/src/com/android/camera/one/v2/camera2proxy/ImageReaderProxy.java @@ -38,7 +38,7 @@ public interface ImageReaderProxy extends SafeCloseable { /** * See {@link ImageReader.OnImageAvailableListener#onImageAvailable} */ - public void onImageAvailable(@Nonnull ImageReaderProxy imageReader); + public void onImageAvailable(); } /** diff --git a/src/com/android/camera/one/v2/sharedimagereader/imagedistributor/ImageDistributorFactory.java b/src/com/android/camera/one/v2/sharedimagereader/imagedistributor/ImageDistributorFactory.java index 02ae3515e..86c4e6e65 100644 --- a/src/com/android/camera/one/v2/sharedimagereader/imagedistributor/ImageDistributorFactory.java +++ b/src/com/android/camera/one/v2/sharedimagereader/imagedistributor/ImageDistributorFactory.java @@ -46,7 +46,7 @@ public class ImageDistributorFactory { Handler imageReaderHandler = handlerFactory.create(lifetime, "ImageDistributor"); imageReader.setOnImageAvailableListener( - new ImageDistributorOnImageAvailableListener(mImageDistributor), + new ImageDistributorOnImageAvailableListener(imageReader, mImageDistributor), imageReaderHandler); } diff --git a/src/com/android/camera/one/v2/sharedimagereader/imagedistributor/ImageDistributorOnImageAvailableListener.java b/src/com/android/camera/one/v2/sharedimagereader/imagedistributor/ImageDistributorOnImageAvailableListener.java index 3bbfbd4a8..8b1ea58e5 100644 --- a/src/com/android/camera/one/v2/sharedimagereader/imagedistributor/ImageDistributorOnImageAvailableListener.java +++ b/src/com/android/camera/one/v2/sharedimagereader/imagedistributor/ImageDistributorOnImageAvailableListener.java @@ -16,35 +16,34 @@ package com.android.camera.one.v2.sharedimagereader.imagedistributor; -import com.android.camera.debug.Log; -import com.android.camera.one.v2.camera2proxy.ForwardingImageProxy; import com.android.camera.one.v2.camera2proxy.ImageProxy; import com.android.camera.one.v2.camera2proxy.ImageReaderProxy; -import java.util.concurrent.atomic.AtomicInteger; - -import javax.annotation.Nonnull; import javax.annotation.ParametersAreNonnullByDefault; /** - * Connects an {@link ImageReaderProxy} to an {@link ImageDistributor} with a new - * thread to handle image availability callbacks. + * Connects an {@link ImageReaderProxy} to an {@link ImageDistributor} with a + * new thread to handle image availability callbacks. */ @ParametersAreNonnullByDefault class ImageDistributorOnImageAvailableListener implements ImageReaderProxy.OnImageAvailableListener { private final ImageDistributorImpl mImageDistributor; + private final ImageReaderProxy mImageReader; /** + * @param imageReader The image reader to retrieve images from. * @param imageDistributor The image distributor to send images to. */ - public ImageDistributorOnImageAvailableListener(ImageDistributorImpl imageDistributor) { + public ImageDistributorOnImageAvailableListener(ImageReaderProxy imageReader, + ImageDistributorImpl imageDistributor) { mImageDistributor = imageDistributor; + mImageReader = imageReader; } @Override - public void onImageAvailable(@Nonnull ImageReaderProxy imageReader) { - ImageProxy nextImage = imageReader.acquireNextImage(); + public void onImageAvailable() { + ImageProxy nextImage = mImageReader.acquireNextImage(); if (nextImage != null) { mImageDistributor.distributeImage(new SingleCloseImageProxy(nextImage)); } diff --git a/src/com/android/camera/one/v2/sharedimagereader/imagedistributor/RefCountedImageProxy.java b/src/com/android/camera/one/v2/sharedimagereader/imagedistributor/RefCountedImageProxy.java index 03e53f345..40f6b60c6 100644 --- a/src/com/android/camera/one/v2/sharedimagereader/imagedistributor/RefCountedImageProxy.java +++ b/src/com/android/camera/one/v2/sharedimagereader/imagedistributor/RefCountedImageProxy.java @@ -20,10 +20,13 @@ import com.android.camera.async.RefCountBase; import com.android.camera.one.v2.camera2proxy.ForwardingImageProxy; import com.android.camera.one.v2.camera2proxy.ImageProxy; +import javax.annotation.concurrent.ThreadSafe; + /** * Wraps ImageProxy with reference counting, starting with a fixed number of * references. */ +@ThreadSafe class RefCountedImageProxy extends ForwardingImageProxy { private final RefCountBase mRefCount; diff --git a/src/com/android/camera/one/v2/sharedimagereader/metadatasynchronizer/MetadataReleasingImageQueue.java b/src/com/android/camera/one/v2/sharedimagereader/metadatasynchronizer/MetadataReleasingImageQueue.java index 30613e2e5..9225b8a4b 100644 --- a/src/com/android/camera/one/v2/sharedimagereader/metadatasynchronizer/MetadataReleasingImageQueue.java +++ b/src/com/android/camera/one/v2/sharedimagereader/metadatasynchronizer/MetadataReleasingImageQueue.java @@ -22,12 +22,14 @@ import com.android.camera.one.v2.camera2proxy.ImageProxy; import javax.annotation.Nonnull; import javax.annotation.ParametersAreNonnullByDefault; +import javax.annotation.concurrent.ThreadSafe; /** * Wraps an output queue of images by wrapping each image to track when they are * closed. When images are closed, their associated metadata entry is freed to * not leak memory. */ +@ThreadSafe @ParametersAreNonnullByDefault public class MetadataReleasingImageQueue implements BufferQueueController { private class MetadataReleasingImageProxy extends ForwardingImageProxy { diff --git a/src/com/android/camera/one/v2/sharedimagereader/util/TicketImageProxy.java b/src/com/android/camera/one/v2/sharedimagereader/util/TicketImageProxy.java index 1db24dc74..328d5c6c0 100644 --- a/src/com/android/camera/one/v2/sharedimagereader/util/TicketImageProxy.java +++ b/src/com/android/camera/one/v2/sharedimagereader/util/TicketImageProxy.java @@ -22,9 +22,12 @@ import com.android.camera.one.v2.sharedimagereader.ticketpool.Ticket; import java.util.concurrent.atomic.AtomicBoolean; +import javax.annotation.concurrent.ThreadSafe; + /** * Combines an {@link ImageProxy} with a {@link Ticket}. */ +@ThreadSafe public class TicketImageProxy extends ForwardingImageProxy { private final Ticket mTicket; private final AtomicBoolean mClosed;