Merge "SnapdragonCamera: Fix compile error on Android M" into camera.lnx.1.0-dev.1.0
diff --git a/AndroidManifest.xml b/AndroidManifest.xml
old mode 100644
new mode 100755
index 572ae95..11046e2
--- a/AndroidManifest.xml
+++ b/AndroidManifest.xml
@@ -54,6 +54,10 @@
             android:taskAffinity="com.android.camera.CameraActivity"
             android:theme="@style/Theme.Camera"
             android:windowSoftInputMode="stateAlwaysHidden|adjustPan" >
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN" />
+                <category android:name="android.intent.category.LAUNCHER" />
+            </intent-filter>
 
             <meta-data
                 android:name="com.android.keyguard.layout"
diff --git a/res/values/camera2arrays.xml b/res/values/camera2arrays.xml
index 204638c..efa9b20 100644
--- a/res/values/camera2arrays.xml
+++ b/res/values/camera2arrays.xml
@@ -1030,4 +1030,15 @@
         <item>@string/pref_camera2_auto_hdr_entry_enable</item>
         <item>@string/pref_camera2_auto_hdr_entry_disable</item>
     </string-array>
+
+    <string-array name="pref_camera2_saveraw_entries" translatable="false">
+        <item>@string/pref_camera2_saveraw_entry_disable</item>
+        <item>@string/pref_camera2_saveraw_entry_enable</item>
+    </string-array>
+
+    <string-array name="pref_camera2_saveraw_entryvalues" translatable="false">
+        <item>@string/pref_camera2_saveraw_value_disable</item>
+        <item>@string/pref_camera2_saveraw_value_enable</item>
+    </string-array>
+
 </resources>
diff --git a/res/values/qcomstrings.xml b/res/values/qcomstrings.xml
index 70b211d..baf472c 100644
--- a/res/values/qcomstrings.xml
+++ b/res/values/qcomstrings.xml
@@ -1050,6 +1050,14 @@
     <string name="pref_camera2_timer_value_2sec" translatable="false">2</string>
     <string name="pref_camera2_timer_value_5sec" translatable="false">5</string>
     <string name="pref_camera2_timer_value_10sec" translatable="false">10</string>
+    <string name="pref_camera2_saveraw_default">disable</string>
+    <string name="pref_camera2_saveraw_title">Save with RAW format</string>
+    <string name="pref_camera2_saveraw_value_disable" translatable="false">disable</string>
+    <string name="pref_camera2_saveraw_value_enable" translatable="false">enable</string>
+
+    <string name="pref_camera2_saveraw_entry_disable">disable</string>
+    <string name="pref_camera2_saveraw_entry_enable">enable</string>
+
     <string name="panocapture_direction_is_not_determined" translatable="true">Move constantly in one direciton.</string>
     <string name="panocapture_intro" translatable="true">Press camera button to start.</string>
     <string name="panocapture_direction_is_changed" translatable="true">Direction is changed. Stopping now...</string>
diff --git a/res/xml/capture_preferences.xml b/res/xml/capture_preferences.xml
index 6789397..8dd3eb7 100644
--- a/res/xml/capture_preferences.xml
+++ b/res/xml/capture_preferences.xml
@@ -335,4 +335,11 @@
         camera:entryValues="@array/pref_camera2_hdr_entryvalues"
         camera:key="pref_camera2_hdr_key"
         camera:title="@string/pref_camera2_hdr_title" />
+
+    <ListPreference
+        camera:key="pref_camera2_saveraw_key"
+        camera:defaultValue="@string/pref_camera2_saveraw_default"
+        camera:title="@string/pref_camera2_saveraw_title"
+        camera:entries="@array/pref_camera2_saveraw_entries"
+        camera:entryValues="@array/pref_camera2_saveraw_entryvalues" />
 </PreferenceGroup>
diff --git a/res/xml/setting_menu_preferences.xml b/res/xml/setting_menu_preferences.xml
index f09ca87..d781310 100644
--- a/res/xml/setting_menu_preferences.xml
+++ b/res/xml/setting_menu_preferences.xml
@@ -275,7 +275,7 @@
             android:summary="Version"
             android:title="Version Info" />
     </PreferenceCategory>
-    
+
     <PreferenceCategory
         android:key="developer"
         android:layout="@layout/preferences_category"
@@ -329,5 +329,14 @@
             android:layout="@layout/preference"
             android:summary="%s"
             android:title="@string/pref_camera2_auto_hdr_title" />
+
+        <ListPreference
+            android:defaultValue="@string/pref_camera2_saveraw_default"
+            android:entries="@array/pref_camera2_saveraw_entries"
+            android:entryValues="@array/pref_camera2_saveraw_entryvalues"
+            android:key="pref_camera2_saveraw_key"
+            android:layout="@layout/preference"
+            android:summary="%s"
+            android:title="@string/pref_camera2_saveraw_title" />
     </PreferenceCategory>
 </PreferenceScreen>
diff --git a/src/com/android/camera/CameraSettings.java b/src/com/android/camera/CameraSettings.java
index 97c4eae..069fb87 100644
--- a/src/com/android/camera/CameraSettings.java
+++ b/src/com/android/camera/CameraSettings.java
@@ -1027,10 +1027,6 @@
             return;
         }
 
-        if (numOfCameras > 2 ) {
-            numOfCameras = 2;
-        }
-
         CharSequence[] entryValues = new CharSequence[numOfCameras];
         for (int i = 0; i < numOfCameras; ++i) {
             entryValues[i] = "" + i;
diff --git a/src/com/android/camera/CaptureModule.java b/src/com/android/camera/CaptureModule.java
index 36251aa..33fd686 100755
--- a/src/com/android/camera/CaptureModule.java
+++ b/src/com/android/camera/CaptureModule.java
@@ -294,6 +294,7 @@
     private Uri mSaveUri;
     private boolean mQuickCapture;
     private byte[] mJpegImageData;
+    private boolean mSaveRaw = false;
 
     /**
      * A {@link CameraCaptureSession } for camera preview.
@@ -325,6 +326,7 @@
      * An {@link ImageReader} that handles still image capture.
      */
     private ImageReader[] mImageReader = new ImageReader[MAX_NUM_CAM];
+    private ImageReader[] mRawImageReader = new ImageReader[MAX_NUM_CAM];
     private NamedImages mNamedImages;
     private ContentResolver mContentResolver;
     private byte[] mLastJpegData;
@@ -372,6 +374,7 @@
 
     private MediaActionSound mSound;
     private Size mSupportedMaxPictureSize;
+    private Size mSupportedRawPictureSize;
 
 
     private class SelfieThread extends Thread {
@@ -777,7 +780,7 @@
         return false;
     }
 
-    private int getCameraMode() {
+    public int getCameraMode() {
         String value = mSettingsManager.getValue(SettingsManager.KEY_SCENE_MODE);
         if (value != null && value.equals(SettingsManager.SCENE_MODE_DUAL_STRING)) return DUAL_MODE;
         value = mSettingsManager.getValue(SettingsManager.KEY_MONO_ONLY);
@@ -791,6 +794,12 @@
         return isBackCamera() && getCameraMode() == DUAL_MODE && value.equals("on");
     }
 
+    private boolean isRawCaptureOn() {
+        String value = mSettingsManager.getValue(SettingsManager.KEY_SAVERAW);
+        if (value == null) return  false;
+        return value.equals("enable");
+    }
+
     private boolean isMpoOn() {
         String value = mSettingsManager.getValue(SettingsManager.KEY_MPO);
         if (value == null) return false;
@@ -1066,10 +1075,16 @@
                     list.add(surs);
                 }
                 list.add(mImageReader[id].getSurface());
+                if (mSaveRaw) {
+                    list.add(mRawImageReader[id].getSurface());
+                }
                 if(mChosenImageFormat == ImageFormat.YUV_420_888 || mChosenImageFormat == ImageFormat.PRIVATE) {
                     if (mPostProcessor.isZSLEnabled()) {
                         mPreviewRequestBuilder[id].addTarget(mImageReader[id].getSurface());
                         list.add(mPostProcessor.getZSLReprocessImageReader().getSurface());
+                        if (mSaveRaw) {
+                            mPreviewRequestBuilder[id].addTarget(mRawImageReader[id].getSurface());
+                        }
                         mCameraDevice[id].createReprocessableCaptureSession(new InputConfiguration(mImageReader[id].getWidth(),
                                 mImageReader[id].getHeight(), mImageReader[id].getImageFormat()), list, captureSessionCallback, null);
                     } else {
@@ -1458,6 +1473,9 @@
                 checkAndPlayShutterSound(id);
                 mCaptureSession[id].stopRepeating();
                 captureBuilder.addTarget(mImageReader[id].getSurface());
+                if (mSaveRaw) {
+                    captureBuilder.addTarget(mRawImageReader[id].getSurface());
+                }
                 mPostProcessor.onStartCapturing();
                 if(mPostProcessor.isManualMode()) {
                     mPostProcessor.manualCapture(captureBuilder, mCaptureSession[id], mCaptureCallbackHandler);
@@ -1467,6 +1485,9 @@
                 }
             } else {
                 captureBuilder.addTarget(mImageReader[id].getSurface());
+                if (mSaveRaw) {
+                    captureBuilder.addTarget(mRawImageReader[id].getSurface());
+                }
                 mCaptureSession[id].stopRepeating();
 
                 if (mLongshotActive) {
@@ -1665,19 +1686,24 @@
                             mImageReader[i] = ImageReader.newInstance(mPictureSize.getWidth(),
                                     mPictureSize.getHeight(), imageFormat, mPostProcessor.getMaxRequiredImageNum());
                         }
+                        if (mSaveRaw) {
+                            mRawImageReader[i] = ImageReader.newInstance(mSupportedRawPictureSize.getWidth(),
+                                    mSupportedRawPictureSize.getHeight(), ImageFormat.RAW10, mPostProcessor.getMaxRequiredImageNum());
+                            mPostProcessor.setRawImageReader(mRawImageReader[i]);
+                        }
                         mImageReader[i].setOnImageAvailableListener(mPostProcessor.getImageHandler(), mImageAvailableHandler);
                         mPostProcessor.onImageReaderReady(mImageReader[i], mSupportedMaxPictureSize, mPictureSize);
                     } else {
                         mImageReader[i] = ImageReader.newInstance(mPictureSize.getWidth(),
                                 mPictureSize.getHeight(), imageFormat, PersistUtil.getLongshotShotLimit());
 
-                        mImageReader[i].setOnImageAvailableListener(new ImageAvailableListener(i) {
+                        ImageAvailableListener listener = new ImageAvailableListener(i) {
                             @Override
                             public void onImageAvailable(ImageReader reader) {
                                 Log.d(TAG, "image available for cam: " + mCamId);
                                 Image image = reader.acquireNextImage();
 
-                                if(isMpoOn()) {
+                                if (isMpoOn()) {
                                     mMpoSaveHandler.obtainMessage(
                                             MpoSaveHandler.MSG_NEW_IMG, mCamId, 0, image).sendToTarget();
                                 } else {
@@ -1689,32 +1715,44 @@
 
                                     byte[] bytes = getJpegData(image);
 
-                                    ExifInterface exif = Exif.getExif(bytes);
-                                    int orientation = Exif.getOrientation(exif);
-
-                                    if (getCameraMode() != CaptureModule.INTENT_MODE_NORMAL) {
-                                        mJpegImageData = bytes;
-                                        if (!mQuickCapture) {
-                                            showCapturedReview(bytes, orientation,
-                                                    mPostProcessor.isSelfieMirrorOn());
-                                        } else {
-                                            onCaptureDone();
-                                        }
+                                    if (image.getFormat() == ImageFormat.RAW10) {
+                                        mActivity.getMediaSaveService().addRawImage(bytes, title,
+                                                "raw");
                                     } else {
-                                        mActivity.getMediaSaveService().addImage(bytes, title, date,
-                                                null, image.getWidth(), image.getHeight(), orientation, null,
-                                                mOnMediaSavedListener, mContentResolver, "jpeg");
+                                        ExifInterface exif = Exif.getExif(bytes);
+                                        int orientation = Exif.getOrientation(exif);
 
-                                        if(mLongshotActive) {
-                                            mLastJpegData = bytes;
+                                        if (getCameraMode() != CaptureModule.INTENT_MODE_NORMAL) {
+                                            mJpegImageData = bytes;
+                                            if (!mQuickCapture) {
+                                                showCapturedReview(bytes, orientation,
+                                                        mPostProcessor.isSelfieMirrorOn());
+                                            } else {
+                                                onCaptureDone();
+                                            }
                                         } else {
-                                            mActivity.updateThumbnail(bytes);
+                                            mActivity.getMediaSaveService().addImage(bytes, title, date,
+                                                    null, image.getWidth(), image.getHeight(), orientation, null,
+                                                    mOnMediaSavedListener, mContentResolver, "jpeg");
+
+                                            if (mLongshotActive) {
+                                                mLastJpegData = bytes;
+                                            } else {
+                                                mActivity.updateThumbnail(bytes);
+                                            }
                                         }
+                                        image.close();
                                     }
-                                    image.close();
                                 }
                             }
-                        }, mImageAvailableHandler);
+                        };
+                        mImageReader[i].setOnImageAvailableListener(listener, mImageAvailableHandler);
+
+                        if (mSaveRaw) {
+                            mRawImageReader[i] = ImageReader.newInstance(mSupportedRawPictureSize.getWidth(),
+                                    mSupportedRawPictureSize.getHeight(), ImageFormat.RAW10, PersistUtil.getLongshotShotLimit());
+                            mRawImageReader[i].setOnImageAvailableListener(listener, mImageAvailableHandler);
+                        }
                     }
                 }
             }
@@ -2257,12 +2295,14 @@
             if(flashMode != null && flashMode.equalsIgnoreCase("on")) {
                 isFlashOn = true;
             }
+
+            mSaveRaw = isRawCaptureOn();
             if (scene != null) {
                 int mode = Integer.parseInt(scene);
                 Log.d(TAG, "Chosen postproc filter id : " + getPostProcFilterId(mode));
-                mPostProcessor.onOpen(getPostProcFilterId(mode), isFlashOn, isTrackingFocusSettingOn(), isMakeupOn, isSelfieMirrorOn);
+                mPostProcessor.onOpen(getPostProcFilterId(mode), isFlashOn, isTrackingFocusSettingOn(), isMakeupOn, isSelfieMirrorOn, mSaveRaw);
             } else {
-                mPostProcessor.onOpen(PostProcessor.FILTER_NONE, isFlashOn, isTrackingFocusSettingOn(), isMakeupOn, isSelfieMirrorOn);
+                mPostProcessor.onOpen(PostProcessor.FILTER_NONE, isFlashOn, isTrackingFocusSettingOn(), isMakeupOn, isSelfieMirrorOn, mSaveRaw);
             }
         }
         if(mFrameProcessor != null) {
@@ -2331,6 +2371,9 @@
         setProModeVisible();
 
         String scene = mSettingsManager.getValue(SettingsManager.KEY_SCENE_MODE);
+        if (Integer.parseInt(scene) != SettingsManager.SCENE_MODE_UBIFOCUS_INT) {
+            setRefocusLastTaken(false);
+        }
         if(isPanoSetting(scene)) {
             if (mIntentMode != CaptureModule.INTENT_MODE_NORMAL) {
                 mSettingsManager.setValue(
@@ -2358,6 +2401,8 @@
         if(mFrameProcessor != null){
             mFrameProcessor.onDestory();
         }
+        mSettingsManager.unregisterListener(this);
+        mSettingsManager.unregisterListener(mUI);
     }
 
     @Override
@@ -2881,6 +2926,9 @@
         Size[] prevSizes = mSettingsManager.getSupportedOutputSize(getMainCameraId(),
                 SurfaceHolder.class);
         mSupportedMaxPictureSize = prevSizes[0];
+        Size[] rawSize = mSettingsManager.getSupportedOutputSize(getMainCameraId(),
+                    ImageFormat.RAW10);
+        mSupportedRawPictureSize = rawSize[0];
         mPreviewSize = getOptimalPreviewSize(mPictureSize, prevSizes, screenSize.x, screenSize.y);
         Size[] thumbSizes = mSettingsManager.getSupportedThumbnailSizes(getMainCameraId());
         mPictureThumbSize = getOptimalPreviewSize(mPictureSize, thumbSizes, 0, 0); // get largest thumb size
@@ -4224,6 +4272,7 @@
                     updateVideoFlash();
                     return;
                 case SettingsManager.KEY_FLASH_MODE:
+                case SettingsManager.KEY_SAVERAW:
                     if (count == 0) restartSession(false);
                     return;
                 case SettingsManager.KEY_SCENE_MODE:
@@ -4321,6 +4370,7 @@
         onPauseAfterSuper();
         onResumeBeforeSuper();
         onResumeAfterSuper();
+        setRefocusLastTaken(false);
     }
 
     public void restartSession(boolean isSurfaceChanged) {
diff --git a/src/com/android/camera/CaptureUI.java b/src/com/android/camera/CaptureUI.java
index 8bbbf17..e8c3c6b 100755
--- a/src/com/android/camera/CaptureUI.java
+++ b/src/com/android/camera/CaptureUI.java
@@ -624,7 +624,7 @@
     }
 
     public void openSettingsMenu() {
-        if (mPreviewLayout.getVisibility() == View.VISIBLE) {
+        if (mPreviewLayout != null && mPreviewLayout.getVisibility() == View.VISIBLE) {
             return;
         }
         clearFocus();
@@ -1665,6 +1665,9 @@
         boolean changed = (width != mPreviewWidth) || (height != mPreviewHeight);
         mPreviewWidth = width;
         mPreviewHeight = height;
+        if (changed) {
+            showSurfaceView();
+        }
         return changed;
     }
 
diff --git a/src/com/android/camera/MediaSaveService.java b/src/com/android/camera/MediaSaveService.java
old mode 100644
new mode 100755
index 0daad1b..2d7247d
--- a/src/com/android/camera/MediaSaveService.java
+++ b/src/com/android/camera/MediaSaveService.java
@@ -134,6 +134,20 @@
         t.execute();
     }
 
+    public void addRawImage(final byte[] data, String title, String pictureFormat) {
+        if (isQueueFull()) {
+            Log.e(TAG, "Cannot add image when the queue is full");
+            return;
+        }
+        RawImageSaveTask t = new RawImageSaveTask(data, title, pictureFormat);
+
+        mMemoryUse += data.length;
+        if (isQueueFull()) {
+            onQueueFull();
+        }
+        t.execute();
+    }
+
     public void addImage(final byte[] data, String title, long date, Location loc,
                          int orientation, ExifInterface exif,
                          OnMediaSavedListener l, ContentResolver resolver) {
@@ -250,6 +264,36 @@
         }
     }
 
+    private class RawImageSaveTask extends AsyncTask<Void, Void, Long> {
+        private byte[] data;
+        private String title;
+        private String pictureFormat;
+
+        public RawImageSaveTask(byte[] data, String title, String pictureFormat) {
+            this.data = data;
+            this.title = title;
+            this.pictureFormat = pictureFormat;
+        }
+
+        @Override
+        protected void onPreExecute() {
+            super.onPreExecute();
+        }
+
+        @Override
+        protected Long doInBackground(Void... params) {
+            long length = Storage.addRawImage(title, data, pictureFormat);
+            return new Long(length);
+        }
+
+        @Override
+        protected void onPostExecute(Long l) {
+            boolean previouslyFull = isQueueFull();
+            mMemoryUse -= data.length;
+            if (isQueueFull() != previouslyFull) onQueueAvailable();
+        }
+    }
+
     private class ImageSaveTask extends AsyncTask <Void, Void, Uri> {
         private byte[] data;
         private String title;
diff --git a/src/com/android/camera/SettingsManager.java b/src/com/android/camera/SettingsManager.java
index 3ce6460..f553c60 100644
--- a/src/com/android/camera/SettingsManager.java
+++ b/src/com/android/camera/SettingsManager.java
@@ -143,6 +143,8 @@
     public static final String KEY_ANTI_BANDING_LEVEL = "pref_camera2_anti_banding_level_key";
     public static final String KEY_HISTOGRAM = "pref_camera2_histogram_key";
     public static final String KEY_HDR = "pref_camera2_hdr_key";
+    public static final String KEY_SAVERAW = "pref_camera2_saveraw_key";
+
     private static final String TAG = "SnapCam_SettingsManager";
 
     private static SettingsManager sInstance;
diff --git a/src/com/android/camera/Storage.java b/src/com/android/camera/Storage.java
old mode 100644
new mode 100755
index 6239494..13f65e1
--- a/src/com/android/camera/Storage.java
+++ b/src/com/android/camera/Storage.java
@@ -168,6 +168,18 @@
          return insertImage(resolver, values);
     }
 
+    public static long addRawImage(String title, byte[] data,
+                                  String mimeType) {
+        String path = generateFilepath(title, mimeType);
+        int size = writeFile(path, data, null, mimeType);
+        // Try to get the real image size after add exif.
+        File f = new File(path);
+        if (f.exists() && f.isFile()) {
+            size = (int) f.length();
+        }
+        return size;
+    }
+
     // Overwrites the file and updates the MediaStore, or inserts the image if
     // one does not already exist.
     public static void updateImage(Uri imageUri, ContentResolver resolver, String title, long date,
diff --git a/src/com/android/camera/imageprocessor/PostProcessor.java b/src/com/android/camera/imageprocessor/PostProcessor.java
index 73a0049..c64684f 100755
--- a/src/com/android/camera/imageprocessor/PostProcessor.java
+++ b/src/com/android/camera/imageprocessor/PostProcessor.java
@@ -130,6 +130,7 @@
     private ImageReader mImageReader;
     private ImageReader mZSLReprocessImageReader;
     private boolean mUseZSL = true;
+    private boolean mSaveRaw = false;
     private Handler mZSLHandler;
     private HandlerThread mZSLHandlerThread;
     private Handler mSavingHander;
@@ -139,7 +140,7 @@
     private TotalCaptureResult mZSLFallOffResult = null;
     private boolean mIsZSLFallOff = false;
     private TotalCaptureResult mLatestResultForLongShot = null;
-    private LinkedList<Image> mFallOffImages = new LinkedList<Image>();
+    private LinkedList<ZSLQueue.ImageItem> mFallOffImages = new LinkedList<ZSLQueue.ImageItem>();
     private int mPendingContinuousRequestCount = 0;
     public int mMaxRequiredImageNum;
 
@@ -165,10 +166,18 @@
 
     private class ImageWrapper {
         Image mImage;
+        Image mRawImage;
         boolean mIsTaken;
 
         public ImageWrapper(Image image) {
             mImage = image;
+            mRawImage = null;
+            mIsTaken = false;
+        }
+
+        public ImageWrapper(Image image, Image rawImage) {
+            mImage = image;
+            mRawImage = rawImage;
             mIsTaken = false;
         }
 
@@ -180,23 +189,31 @@
             mIsTaken = true;
             return mImage;
         }
+
+        public Image getRawImage() {
+            return mRawImage;
+        }
     }
 
     private void clearFallOffImage() {
-        for(Image im: mFallOffImages ) {
+        for(ZSLQueue.ImageItem item: mFallOffImages ) {
             try {
-                im.close();
+                item.getImage().close();
+                Image raw = item.getRawImage();
+                if (raw != null) {
+                    raw.close();
+                }
             } catch(Exception e) {
             }
         }
         mFallOffImages.clear();
     }
 
-    private Image findFallOffImage(long timestamp) {
-        Image foundImage = null;
-        for(Image im: mFallOffImages ) {
-            if(im.getTimestamp() == timestamp) {
-                foundImage = im;
+    private ZSLQueue.ImageItem findFallOffImage(long timestamp) {
+        ZSLQueue.ImageItem foundImage = null;
+        for(ZSLQueue.ImageItem item: mFallOffImages ) {
+            if(item.getImage().getTimestamp() == timestamp) {
+                foundImage = item;
                 break;
             }
         }
@@ -206,12 +223,16 @@
         return foundImage;
     }
 
-    private void addFallOffImage(Image image) {
-        mFallOffImages.add(image);
+    private void addFallOffImage(ZSLQueue.ImageItem item) {
+        mFallOffImages.add(item);
         if(mFallOffImages.size() >= MAX_REQUIRED_IMAGE_NUM - 1) {
-            Image im = mFallOffImages.getFirst();
+            ZSLQueue.ImageItem it = mFallOffImages.getFirst();
             try {
-                im.close();
+                it.getImage().close();
+                Image raw = item.getRawImage();
+                if (raw != null) {
+                    raw.close();
+                }
             } catch(Exception e) {
             }
             mFallOffImages.removeFirst();
@@ -220,6 +241,7 @@
 
     class ImageHandlerTask implements Runnable, ImageReader.OnImageAvailableListener {
         private ImageWrapper mImageWrapper = null;
+        private ImageReader mRawImageReader = null;
         Semaphore mMutureLock = new Semaphore(1);
 
         @Override
@@ -228,24 +250,42 @@
                 if(mUseZSL) {
                     if(mController.isLongShotActive() && mPendingContinuousRequestCount > 0) {
                         Image image = reader.acquireNextImage();
+                        Image rawImage = null;
+                        if (mSaveRaw && mRawImageReader != null) {
+                            rawImage = mRawImageReader.acquireNextImage();
+                        }
                         ZSLQueue.ImageItem item = new ZSLQueue.ImageItem();
-                        item.setImage(image);
+                        item.setImage(image, rawImage);
                         if(onContinuousZSLImage(item, true)) {
                             image.close();
+                            if (rawImage != null) {
+                                rawImage.close();
+                            }
                         }
                         return;
                     }
                     if(mIsZSLFallOff) {
                         Image image = reader.acquireNextImage();
+                        Image rawImage = null;
+                        if (mSaveRaw && mRawImageReader != null) {
+                            rawImage = mRawImageReader.acquireNextImage();
+                        }
+                        ZSLQueue.ImageItem imageItem = new ZSLQueue.ImageItem();
+                        imageItem.setImage(image,rawImage);
                         if(mZSLFallOffResult == null) {
-                            addFallOffImage(image);
+                            addFallOffImage(imageItem);
                             return;
                         }
-                        addFallOffImage(image);
-                        Image foundImage = findFallOffImage(mZSLFallOffResult.get(CaptureResult.SENSOR_TIMESTAMP).longValue());
-                        if(foundImage != null) {
+                        addFallOffImage(imageItem);
+                        ZSLQueue.ImageItem foundImage = findFallOffImage(
+                                mZSLFallOffResult.get(CaptureResult.SENSOR_TIMESTAMP).longValue());
+                        if(foundImage != null && foundImage.getImage() != null) {
                             Log.d(TAG,"ZSL fall off image is found");
-                            reprocessImage(foundImage, mZSLFallOffResult);
+                            reprocessImage(foundImage.getImage(), mZSLFallOffResult);
+                            Image raw = foundImage.getRawImage();
+                            if (raw != null) {
+                                onRawImageToProcess(raw);
+                            }
                             mIsZSLFallOff = false;
                             clearFallOffImage();
                             mZSLFallOffResult = null;
@@ -256,18 +296,33 @@
                     }
 
                     Image image = reader.acquireLatestImage();
+                    Image rawImage = null;
+                    if (mSaveRaw && mRawImageReader != null) {
+                        rawImage = mRawImageReader.acquireLatestImage();
+                    }
+
                     if (image == null) {
                         return;
                     }
                     if (!mMutureLock.tryAcquire()) {
                         image.close();
+                        if (rawImage != null) {
+                            rawImage.close();
+                        }
                         return;
                     }
                     if (mImageWrapper == null || mImageWrapper.isTaken()) {
-                        mImageWrapper = new ImageWrapper(image);
+                        if (mSaveRaw && rawImage != null) {
+                            mImageWrapper = new ImageWrapper(image, rawImage);
+                        } else {
+                            mImageWrapper = new ImageWrapper(image);
+                        }
                         mMutureLock.release();
                     } else {
                         image.close();
+                        if (rawImage != null) {
+                            rawImage.close();
+                        }
                         mMutureLock.release();
                         return;
                     }
@@ -276,8 +331,15 @@
                     }
                 } else { //Non ZSL case
                     Image image = reader.acquireNextImage();
+                    Image rawImage = null;
                     if(image != null) {
                         onImageToProcess(image);
+                        if (mSaveRaw && mRawImageReader != null) {
+                            rawImage = mRawImageReader.acquireNextImage();
+                        }
+                        if (rawImage != null) {
+                            onRawImageToProcess(rawImage);
+                        }
                     }
                 }
             } catch (IllegalStateException e) {
@@ -290,18 +352,27 @@
 
         @Override
         public void run() {   //Only ZSL case
-           Image image = mImageWrapper.getImage();
+            Image image = mImageWrapper.getImage();
+            Image rawImage = mImageWrapper.getRawImage();
             try {
                 mMutureLock.acquire();
                 if (mUseZSL) {
                     if (mZSLQueue != null) {
-                        mZSLQueue.add(image);
+                        mZSLQueue.add(image, rawImage);
                     }
                 }
                 mMutureLock.release();
             } catch (InterruptedException e) {
             }
         }
+
+        public void setRawImageReader(ImageReader rawImageReader) {
+            mRawImageReader = rawImageReader;
+        }
+    }
+
+    public void setRawImageReader(ImageReader rawImageReader) {
+        mImageHandlerTask.setRawImageReader(rawImageReader);
     }
 
     public void onMetaAvailable(TotalCaptureResult metadata) {
@@ -379,6 +450,9 @@
         if (imageItem != null) {
             if(DEBUG_ZSL) Log.d(TAG,"Got the item from the queue");
             reprocessImage(imageItem.getImage(), imageItem.getMetadata());
+            if (mSaveRaw && imageItem.getRawImage() != null) {
+                onRawImageToProcess(imageItem.getRawImage());
+            }
             return true;
         } else {
             if(DEBUG_ZSL) Log.d(TAG, "No good item in queue, register the request for the future");
@@ -397,11 +471,17 @@
         if(isLongShotRequest) {
             if(mLatestResultForLongShot != null) {
                 reprocessImage(imageItem.getImage(), mLatestResultForLongShot);
+                if (imageItem.getRawImage() != null) {
+                    onRawImageToProcess(imageItem.getRawImage());
+                }
                 mPendingContinuousRequestCount--;
                 return true;
             }
         } else {
             reprocessImage(imageItem.getImage(), imageItem.getMetadata());
+            if (imageItem.getRawImage() != null) {
+                onRawImageToProcess(imageItem.getRawImage());
+            }
             return true;
         }
 
@@ -509,6 +589,17 @@
         }
     }
 
+    private void onRawImageToProcess(Image image) {
+        ByteBuffer buffer = image.getPlanes()[0].getBuffer();
+        byte[] data = new byte[buffer.remaining()];
+        buffer.get(data);
+        long captureStartTime = System.currentTimeMillis();
+        mNamedImages.nameNewImage(captureStartTime);
+        PhotoModule.NamedImages.NamedEntity name = mNamedImages.getNextNameEntity();
+        String title = (name == null) ? null : name.title;
+        mActivity.getMediaSaveService().addRawImage(data, title, "raw");
+    }
+
     enum STATUS {
         DEINIT,
         INIT,
@@ -574,12 +665,13 @@
         return false;
     }
 
-    public void onOpen(int postFilterId, boolean isFlashModeOn, boolean isTrackingFocusOn, boolean isMakeupOn, boolean isSelfieMirrorOn) {
+    public void onOpen(int postFilterId, boolean isFlashModeOn, boolean isTrackingFocusOn, boolean isMakeupOn, boolean isSelfieMirrorOn, boolean isSaveRaw) {
         mImageHandlerTask = new ImageHandlerTask();
-
+        mSaveRaw = isSaveRaw;
         if(setFilter(postFilterId) || isFlashModeOn || isTrackingFocusOn || isMakeupOn || isSelfieMirrorOn
                 || PersistUtil.getCameraZSLDisabled()
-                || SettingsManager.getInstance().isCamera2HDRSupport()) {
+                || SettingsManager.getInstance().isCamera2HDRSupport()
+                || mController.getCameraMode() == CaptureModule.DUAL_MODE) {
             mUseZSL = false;
         } else {
             mUseZSL = true;
diff --git a/src/com/android/camera/imageprocessor/ZSLQueue.java b/src/com/android/camera/imageprocessor/ZSLQueue.java
old mode 100644
new mode 100755
index f50858a..b9dea4e
--- a/src/com/android/camera/imageprocessor/ZSLQueue.java
+++ b/src/com/android/camera/imageprocessor/ZSLQueue.java
@@ -87,7 +87,7 @@
         return -1;
     }
 
-    public void add(Image image) {
+    public void add(Image image, Image rawImage) {
         int lastIndex = -1;
         synchronized (mLock) {
             if(mBuffer == null)
@@ -99,7 +99,7 @@
             }
             if(mBuffer[mImageHead].getMetadata() != null) {
                 if((mBuffer[mImageHead].getMetadata().get(CaptureResult.SENSOR_TIMESTAMP)).longValue() == image.getTimestamp()) {
-                    mBuffer[mImageHead].setImage(image);
+                    mBuffer[mImageHead].setImage(image,rawImage);
                     lastIndex = mImageHead;
                     mImageHead = (mImageHead + 1) % mBuffer.length;
                 } else if((mBuffer[mImageHead].getMetadata().get(CaptureResult.SENSOR_TIMESTAMP)).longValue() > image.getTimestamp()) {
@@ -107,17 +107,17 @@
                 } else {
                     int i = findMeta(image.getTimestamp(), mImageHead);
                     if(i == -1) {
-                        mBuffer[mImageHead].setImage(image);
+                        mBuffer[mImageHead].setImage(image, rawImage);
                         mBuffer[mImageHead].setMetadata(null);
                         mImageHead = (mImageHead + 1) % mBuffer.length;
                     } else {
                         lastIndex = mImageHead = i;
-                        mBuffer[mImageHead].setImage(image);
+                        mBuffer[mImageHead].setImage(image, rawImage);
                         mImageHead = (mImageHead + 1) % mBuffer.length;
                     }
                 }
             } else {
-                mBuffer[mImageHead].setImage(image);
+                mBuffer[mImageHead].setImage(image, rawImage);
                 lastIndex = mImageHead;
                 mImageHead = (mImageHead + 1) % mBuffer.length;
             }
@@ -156,7 +156,7 @@
                 } else {
                     int i = findImage(timestamp, mMetaHead);
                     if(i == -1) {
-                        mBuffer[mMetaHead].setImage(null);
+                        mBuffer[mMetaHead].setImage(null, null);
                         mBuffer[mMetaHead].setMetadata(metadata);
                         mMetaHead = (mMetaHead + 1) % mBuffer.length;
                     } else {
@@ -238,17 +238,24 @@
 
     static class ImageItem {
         private Image mImage = null;
+        private Image mRawImage = null;
         private TotalCaptureResult mMetadata = null;
 
         public Image getImage() {
             return mImage;
         }
 
-        public void setImage(Image image) {
+        public Image getRawImage() {return mRawImage;}
+
+        public void setImage(Image image, Image rawImage) {
             if(mImage != null) {
                 mImage.close();
             }
+            if(mRawImage != null) {
+                mRawImage.close();
+            }
             mImage = image;
+            mRawImage =rawImage;
         }
 
         public TotalCaptureResult getMetadata() {
@@ -263,6 +270,9 @@
             if(mImage != null) {
                 mImage.close();
             }
+            if(mRawImage != null) {
+                mRawImage.close();
+            }
             mImage = null;
         }
 
diff --git a/src/org/codeaurora/snapcam/filter/ClearSightImageProcessor.java b/src/org/codeaurora/snapcam/filter/ClearSightImageProcessor.java
index 944bf08..b938363 100755
--- a/src/org/codeaurora/snapcam/filter/ClearSightImageProcessor.java
+++ b/src/org/codeaurora/snapcam/filter/ClearSightImageProcessor.java
@@ -29,7 +29,6 @@
 
 package org.codeaurora.snapcam.filter;
 
-import java.io.IOException;
 import java.io.ByteArrayOutputStream;
 import java.nio.ByteBuffer;
 import java.util.ArrayDeque;
@@ -146,6 +145,7 @@
     private boolean mDumpImages;
     private boolean mDumpYUV;
     private boolean mIsClosing;
+    private int mFinishReprocessNum;
 
     private static ClearSightImageProcessor mInstance;
 
@@ -459,7 +459,7 @@
         private ArrayDeque<Image> mMonoImages = new ArrayDeque<Image>(
                 mNumBurstCount);
 
-        private SparseLongArray mReprocessingFrames = new SparseLongArray();
+        private SparseLongArray[] mReprocessingFrames = new SparseLongArray[NUM_CAM];
         private ArrayList<CaptureRequest> mReprocessingRequests = new ArrayList<CaptureRequest>();
         private int mReprocessingPairCount;
         private int mReprocessedBayerCount;
@@ -471,6 +471,8 @@
 
         ImageProcessHandler(Looper looper) {
             super(looper);
+            mReprocessingFrames[CAM_TYPE_BAYER] = new SparseLongArray();
+            mReprocessingFrames[CAM_TYPE_MONO] = new SparseLongArray();
         }
 
         @Override
@@ -480,6 +482,7 @@
             switch (msg.what) {
             case MSG_START_CAPTURE:
                 mCaptureDone = false;
+                mFinishReprocessNum = 0;
                 mHasFailures = false;
                 mReprocessingPairCount = 0;
                 mReprocessedBayerCount = 0;
@@ -516,7 +519,8 @@
             Log.d(TAG, "handleTimeout");
             releaseBayerFrames();
             releaseMonoFrames();
-            mReprocessingFrames.clear();
+            mReprocessingFrames[CAM_TYPE_BAYER].clear();
+            mReprocessingFrames[CAM_TYPE_MONO].clear();
             mReprocessingRequests.clear();
 
             removeMessages(MSG_NEW_CAPTURE_RESULT);
@@ -536,10 +540,11 @@
         }
 
         private void processImg(Message msg) {
-            Log.d(TAG, "processImg: " + msg.arg1);
+            int camId = msg.arg1;
+            Log.d(TAG, "processImg: " + camId);
             Image image = (Image) msg.obj;
-            if(mReprocessingFrames.size() > 0
-                    && mReprocessingFrames.indexOfValue(image.getTimestamp()) >= 0) {
+            if(mReprocessingFrames[camId].size() > 0
+                    && mReprocessingFrames[camId].indexOfValue(image.getTimestamp()) >= 0) {
                 // reproc frame
                 processNewReprocessImage(msg);
             } else {
@@ -600,13 +605,31 @@
                 checkForValidFramePairAndReprocess();
             }
 
-            Log.d(TAG, "processNewCaptureEvent - imagestoprocess[bayer] " + mNumImagesToProcess[CAM_TYPE_BAYER] +
-                    " imagestoprocess[mono]: " + mNumImagesToProcess[CAM_TYPE_MONO]);
+
+            Log.d(TAG, "processNewCaptureEvent - " +
+                    "imagestoprocess[bayer] " + mNumImagesToProcess[CAM_TYPE_BAYER] +
+                    " imagestoprocess[mono]: " + mNumImagesToProcess[CAM_TYPE_MONO] +
+                    " mReprocessingPairCount: " + mReprocessingPairCount +
+                    " mNumFrameCount: " + mNumFrameCount +
+                    " mFinishReprocessNum: " + mFinishReprocessNum);
+
+            if ((mNumImagesToProcess[CAM_TYPE_BAYER] == 0
+                    && mNumImagesToProcess[CAM_TYPE_MONO] == 0)
+                    && mReprocessingPairCount != mNumFrameCount) {
+                while (!mBayerFrames.isEmpty() && !mMonoFrames.isEmpty()
+                        && mReprocessingPairCount != mNumFrameCount) {
+                    checkForValidFramePairAndReprocess();
+                }
+            }
 
             if (mReprocessingPairCount == mNumFrameCount ||
                     (mNumImagesToProcess[CAM_TYPE_BAYER] == 0
                     && mNumImagesToProcess[CAM_TYPE_MONO] == 0)) {
                 processFinalPair();
+                if (mReprocessingPairCount != 0 &&
+                        mFinishReprocessNum == mReprocessingPairCount * 2) {
+                    checkReprocessDone();
+                }
             }
         }
 
@@ -683,7 +706,7 @@
                 Long ts = Long.valueOf(reprocImg.mImage.getTimestamp());
                 Integer hash = ts.hashCode();
                 reprocRequest.setTag(hash);
-                mReprocessingFrames.put(hash, ts);
+                mReprocessingFrames[camId].put(hash, ts);
                 Log.d(TAG, "sendReprocessRequest - adding reproc frame - hash: " + hash + ", ts: " + ts);
 
                 mImageWriter[camId].queueInputImage(reprocImg.mImage);
@@ -779,7 +802,8 @@
 
             Image image = (Image) msg.obj;
             long ts = image.getTimestamp();
-            Log.d(TAG, "processNewReprocessImage - cam: " + msg.arg1 + ", ts: " + ts);
+            int camId = msg.arg1;
+            Log.d(TAG, "processNewReprocessImage - cam: " + camId + ", ts: " + ts);
             int frameCount = isBayer?++mReprocessedBayerCount:++mReprocessedMonoCount;
 
             if(mDumpImages) {
@@ -793,7 +817,7 @@
             mClearsightRegisterHandler.obtainMessage(MSG_NEW_IMG,
                     msg.arg1, 0, msg.obj).sendToTarget();
 
-            mReprocessingFrames.removeAt(mReprocessingFrames.indexOfValue(ts));
+            mReprocessingFrames[camId].removeAt(mReprocessingFrames[camId].indexOfValue(ts));
             checkReprocessDone();
         }
 
@@ -809,24 +833,30 @@
                 Log.d(TAG, "reprocess - setReferenceResult: " + msg.obj);
                 ClearSightNativeEngine.getInstance().setReferenceResult(isBayer, result);
             }
-
+            mFinishReprocessNum++;
             checkReprocessDone();
         }
 
         private void processNewReprocessFailure(Message msg) {
-            Log.d(TAG, "processNewReprocessFailure: " + msg.arg1);
+            int camId = msg.arg1;
+            Log.d(TAG, "processNewReprocessFailure: " + camId);
             CaptureFailure failure = (CaptureFailure)msg.obj;
             mReprocessingRequests.remove(failure.getRequest());
-            mReprocessingFrames.delete(msg.arg2);
+            mReprocessingFrames[camId].delete(msg.arg2);
             mHasFailures = true;
+            mFinishReprocessNum++;
             checkReprocessDone();
         }
 
         private void checkReprocessDone() {
-            Log.d(TAG, "checkReprocessDone capture done: " + mCaptureDone
-                    + ", reproc frames: " + mReprocessingFrames.size());
+            Log.d(TAG, "checkReprocessDone capture done: " + mCaptureDone +
+                    ", reproc frames[bay]: " + mReprocessingFrames[CAM_TYPE_BAYER].size() +
+                    ", reproc frames[mono]: " + mReprocessingFrames[CAM_TYPE_MONO].size() +
+                    ", mReprocessingRequests: " + mReprocessingRequests.size());
             // If all burst frames and results have been processed
-            if(mCaptureDone && mReprocessingFrames.size() == 0 && mReprocessingRequests.isEmpty()) {
+            if(mCaptureDone && mReprocessingFrames[CAM_TYPE_BAYER].size() == 0
+                    && mReprocessingFrames[CAM_TYPE_MONO].size() == 0
+                    && mReprocessingRequests.isEmpty()) {
                 mClearsightRegisterHandler.obtainMessage(MSG_END_CAPTURE, mHasFailures?1:0, 0).sendToTarget();
                 removeMessages(MSG_NEW_REPROC_RESULT);
                 removeMessages(MSG_NEW_REPROC_FAIL);