Merge "Handle malformed raw AAC/AMR writer better" into jb-mr1-dev
diff --git a/cmds/stagefright/stream.cpp b/cmds/stagefright/stream.cpp
index 8e7861e..ac88d1f 100644
--- a/cmds/stagefright/stream.cpp
+++ b/cmds/stagefright/stream.cpp
@@ -349,7 +349,10 @@
 
     size_t len = strlen(argv[1]);
     if ((!usemp4 && len >= 3 && !strcasecmp(".ts", &argv[1][len - 3])) ||
-        (usemp4 && len >= 4 && !strcasecmp(".mp4", &argv[1][len - 4]))) {
+        (usemp4 && len >= 4 &&
+         (!strcasecmp(".mp4", &argv[1][len - 4])
+            || !strcasecmp(".3gp", &argv[1][len- 4])
+            || !strcasecmp(".3g2", &argv[1][len- 4])))) {
         int fd = open(argv[1], O_RDONLY);
 
         if (fd < 0) {
diff --git a/drm/libdrmframework/plugins/forward-lock/internal-format/converter/FwdLockConv.c b/drm/libdrmframework/plugins/forward-lock/internal-format/converter/FwdLockConv.c
index 299116d..bb97abc 100644
--- a/drm/libdrmframework/plugins/forward-lock/internal-format/converter/FwdLockConv.c
+++ b/drm/libdrmframework/plugins/forward-lock/internal-format/converter/FwdLockConv.c
@@ -1314,34 +1314,3 @@
     }
     return status;
 }
-
-FwdLockConv_Status_t FwdLockConv_ConvertFile(const char *pInputFilename,
-                                             const char *pOutputFilename,
-                                             off64_t *pErrorPos) {
-    FwdLockConv_Status_t status;
-    if (pErrorPos != NULL) {
-        *pErrorPos = INVALID_OFFSET;
-    }
-    if (pInputFilename == NULL || pOutputFilename == NULL) {
-        status = FwdLockConv_Status_InvalidArgument;
-    } else {
-        int inputFileDesc = open(pInputFilename, O_RDONLY);
-        if (inputFileDesc < 0) {
-            status = FwdLockConv_Status_FileNotFound;
-        } else {
-            int outputFileDesc = open(pOutputFilename, O_CREAT | O_TRUNC | O_WRONLY,
-                                      S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
-            if (outputFileDesc < 0) {
-                status = FwdLockConv_Status_FileCreationFailed;
-            } else {
-                status = FwdLockConv_ConvertOpenFile(inputFileDesc, read, outputFileDesc, write,
-                                                     lseek64, pErrorPos);
-                if (close(outputFileDesc) == 0 && status != FwdLockConv_Status_OK) {
-                    remove(pOutputFilename);
-                }
-            }
-            (void)close(inputFileDesc);
-        }
-    }
-    return status;
-}
diff --git a/drm/libdrmframework/plugins/forward-lock/internal-format/converter/FwdLockConv.h b/drm/libdrmframework/plugins/forward-lock/internal-format/converter/FwdLockConv.h
index e20c0c3..6449d89 100644
--- a/drm/libdrmframework/plugins/forward-lock/internal-format/converter/FwdLockConv.h
+++ b/drm/libdrmframework/plugins/forward-lock/internal-format/converter/FwdLockConv.h
@@ -245,36 +245,6 @@
                                                  FwdLockConv_LSeekFunc_t *fpLSeekFunc,
                                                  off64_t *pErrorPos);
 
-/**
- * Converts an OMA DRM v1 Forward Lock file to the internal Forward Lock file format in pull mode.
- *
- * @param[in] pInputFilename A reference to the input filename.
- * @param[in] pOutputFilename A reference to the output filename.
- * @param[out] pErrorPos
- *   The file position where the error occurred, in the case of a syntax error. May be NULL.
- *
- * @return A status code.
- * @retval FwdLockConv_Status_OK
- * @retval FwdLockConv_Status_InvalidArgument
- * @retval FwdLockConv_Status_OutOfMemory
- * @retval FwdLockConv_Status_FileNotFound
- * @retval FwdLockConv_Status_FileCreationFailed
- * @retval FwdLockConv_Status_FileReadError
- * @retval FwdLockConv_Status_FileWriteError
- * @retval FwdLockConv_Status_FileSeekError
- * @retval FwdLockConv_Status_SyntaxError
- * @retval FwdLockConv_Status_UnsupportedFileFormat
- * @retval FwdLockConv_Status_UnsupportedContentTransferEncoding
- * @retval FwdLockConv_Status_RandomNumberGenerationFailed
- * @retval FwdLockConv_Status_KeyEncryptionFailed
- * @retval FwdLockConv_Status_DataEncryptionFailed
- * @retval FwdLockConv_Status_IntegrityProtectionFailed
- * @retval FwdLockConv_Status_TooManySessions
- */
-FwdLockConv_Status_t FwdLockConv_ConvertFile(const char *pInputFilename,
-                                             const char *pOutputFilename,
-                                             off64_t *pErrorPos);
-
 #ifdef __cplusplus
 }
 #endif
diff --git a/media/libmediaplayerservice/nuplayer/mp4/Parser.cpp b/media/libmediaplayerservice/nuplayer/mp4/Parser.cpp
index b9fe819..7938fa4 100644
--- a/media/libmediaplayerservice/nuplayer/mp4/Parser.cpp
+++ b/media/libmediaplayerservice/nuplayer/mp4/Parser.cpp
@@ -271,6 +271,19 @@
             mBuffer->setRange(0, mBuffer->size());
 
             size_t maxBytesToRead = mBuffer->capacity() - mBuffer->size();
+
+            if (maxBytesToRead < needed) {
+                ALOGI("resizing buffer.");
+
+                sp<ABuffer> newBuffer =
+                    new ABuffer((mBuffer->size() + needed + 1023) & ~1023);
+                memcpy(newBuffer->data(), mBuffer->data(), mBuffer->size());
+                newBuffer->setRange(0, mBuffer->size());
+
+                mBuffer = newBuffer;
+                maxBytesToRead = mBuffer->capacity() - mBuffer->size();
+            }
+
             CHECK_GE(maxBytesToRead, needed);
 
             ssize_t n = mSource->readAt(
@@ -1023,6 +1036,11 @@
         case FOURCC('m', 'p', '4', 'v'):
             format->setString("mime", MEDIA_MIMETYPE_VIDEO_MPEG4);
             break;
+        case FOURCC('s', '2', '6', '3'):
+        case FOURCC('h', '2', '6', '3'):
+        case FOURCC('H', '2', '6', '3'):
+            format->setString("mime", MEDIA_MIMETYPE_VIDEO_H263);
+            break;
         default:
             format->setString("mime", "application/octet-stream");
             break;
@@ -1062,11 +1080,13 @@
         case FOURCC('m', 'p', '4', 'a'):
             format->setString("mime", MEDIA_MIMETYPE_AUDIO_AAC);
             break;
+
         case FOURCC('s', 'a', 'm', 'r'):
             format->setString("mime", MEDIA_MIMETYPE_AUDIO_AMR_NB);
             format->setInt32("channel-count", 1);
             format->setInt32("sample-rate", 8000);
             break;
+
         case FOURCC('s', 'a', 'w', 'b'):
             format->setString("mime", MEDIA_MIMETYPE_AUDIO_AMR_WB);
             format->setInt32("channel-count", 1);
diff --git a/media/libmediaplayerservice/nuplayer/mp4/TrackFragment.cpp b/media/libmediaplayerservice/nuplayer/mp4/TrackFragment.cpp
index 12e71db..a4c31ea 100644
--- a/media/libmediaplayerservice/nuplayer/mp4/TrackFragment.cpp
+++ b/media/libmediaplayerservice/nuplayer/mp4/TrackFragment.cpp
@@ -24,6 +24,7 @@
 #include <media/stagefright/Utils.h>
 #include <media/stagefright/foundation/ABuffer.h>
 #include <media/stagefright/foundation/ADebug.h>
+#include <media/stagefright/foundation/hexdump.h>
 
 namespace android {
 
@@ -79,6 +80,7 @@
 Parser::StaticTrackFragment::StaticTrackFragment()
     : mSampleIndex(0),
       mSampleCount(0),
+      mChunkIndex(0),
       mSampleToChunkIndex(-1),
       mSampleToChunkRemaining(0),
       mPrevChunkIndex(0xffffffff),
@@ -149,37 +151,31 @@
     mSampleInfo.mSampleDescIndex =
         U32_AT(mSampleToChunk->data() + 8 + 12 * mSampleToChunkIndex + 8);
 
-    uint32_t chunkIndex =
-        U32_AT(mSampleToChunk->data() + 8 + 12 * mSampleToChunkIndex);
-
-    CHECK_GE(chunkIndex, 1);
-    --chunkIndex;
-
-    if (chunkIndex != mPrevChunkIndex) {
-        mPrevChunkIndex = chunkIndex;
+    if (mChunkIndex != mPrevChunkIndex) {
+        mPrevChunkIndex = mChunkIndex;
 
         if (mChunkOffsets != NULL) {
             uint32_t entryCount = U32_AT(mChunkOffsets->data() + 4);
 
-            if (chunkIndex >= entryCount) {
+            if (mChunkIndex >= entryCount) {
                 mSampleIndex = mSampleCount;
                 return;
             }
 
             mNextSampleOffset =
-                U32_AT(mChunkOffsets->data() + 8 + 4 * chunkIndex);
+                U32_AT(mChunkOffsets->data() + 8 + 4 * mChunkIndex);
         } else {
             CHECK(mChunkOffsets64 != NULL);
 
             uint32_t entryCount = U32_AT(mChunkOffsets64->data() + 4);
 
-            if (chunkIndex >= entryCount) {
+            if (mChunkIndex >= entryCount) {
                 mSampleIndex = mSampleCount;
                 return;
             }
 
             mNextSampleOffset =
-                U64_AT(mChunkOffsets64->data() + 8 + 8 * chunkIndex);
+                U64_AT(mChunkOffsets64->data() + 8 + 8 * mChunkIndex);
         }
     }
 
@@ -194,14 +190,25 @@
 
     ++mSampleIndex;
     if (--mSampleToChunkRemaining == 0) {
+        ++mChunkIndex;
+
         uint32_t entryCount = U32_AT(mSampleToChunk->data() + 4);
 
-        if ((uint32_t)(mSampleToChunkIndex + 1) == entryCount) {
-            mSampleIndex = mSampleCount;  // EOS.
-            return;
+        // If this is the last entry in the sample to chunk table, we will
+        // stay on this entry.
+        if ((uint32_t)(mSampleToChunkIndex + 1) < entryCount) {
+            uint32_t nextChunkIndex =
+                U32_AT(mSampleToChunk->data() + 8 + 12 * (mSampleToChunkIndex + 1));
+
+            CHECK_GE(nextChunkIndex, 1u);
+            --nextChunkIndex;
+
+            if (mChunkIndex >= nextChunkIndex) {
+                CHECK_EQ(mChunkIndex, nextChunkIndex);
+                ++mSampleToChunkIndex;
+            }
         }
 
-        ++mSampleToChunkIndex;
         mSampleToChunkRemaining =
             U32_AT(mSampleToChunk->data() + 8 + 12 * mSampleToChunkIndex + 4);
     }
@@ -216,46 +223,13 @@
     ptr[3] = x & 0xff;
 }
 
-void Parser::StaticTrackFragment::fixSampleToChunkTableIfNecessary() {
-    if (mSampleToChunk == NULL) {
-        return;
-    }
-    uint32_t entryCount = U32_AT(mSampleToChunk->data() + 4);
-    uint32_t totalSamples = 0;
-    for (uint32_t i = 0; i < entryCount; ++i) {
-        totalSamples += U32_AT(mSampleToChunk->data() + 8 + 12 * i + 4);
-    }
-
-    if (totalSamples < mSampleCount) {
-        // Some samples are not accounted for in the sample-to-chunk
-        // data. Fabricate an extra chunk adjacent to the last one
-        // in the table with the same sample desription index.
-
-        ALOGW("Faking an extra sample-to-chunk entry for %d samples.",
-              mSampleCount - totalSamples);
-
-        uint32_t lastChunkIndex =
-            U32_AT(mSampleToChunk->data() + 8 + 12 * (entryCount - 1));
-
-        uint32_t lastSampleDescriptionIndex =
-            U32_AT(mSampleToChunk->data() + 8 + 12 * (entryCount - 1) + 8);
-
-        uint8_t *ptr = mSampleToChunk->data() + 8 + 12 * entryCount;
-
-        setU32At(ptr, lastChunkIndex + 1);
-        setU32At(ptr + 4, mSampleCount - totalSamples);
-        setU32At(ptr + 8, lastSampleDescriptionIndex);
-        setU32At(mSampleToChunk->data() + 4, entryCount + 1);
-    }
-}
-
 status_t Parser::StaticTrackFragment::signalCompletion() {
-    fixSampleToChunkTableIfNecessary();
-
     mSampleToChunkIndex = 0;
 
-    mSampleToChunkRemaining = (mSampleToChunk == NULL) ? 0 :
-        U32_AT(mSampleToChunk->data() + 8 + 12 * mSampleToChunkIndex + 4);
+    mSampleToChunkRemaining =
+        (mSampleToChunk == NULL)
+            ? 0
+            : U32_AT(mSampleToChunk->data() + 8 + 12 * mSampleToChunkIndex + 4);
 
     updateSampleInfo();
 
@@ -339,7 +313,7 @@
         return ERROR_MALFORMED;
     }
 
-    parser->copyBuffer(&mSampleToChunk, offset, size, 12 /* extra */);
+    parser->copyBuffer(&mSampleToChunk, offset, size);
 
     return OK;
 }
diff --git a/media/libmediaplayerservice/nuplayer/mp4/TrackFragment.h b/media/libmediaplayerservice/nuplayer/mp4/TrackFragment.h
index e5945ac..1498aad 100644
--- a/media/libmediaplayerservice/nuplayer/mp4/TrackFragment.h
+++ b/media/libmediaplayerservice/nuplayer/mp4/TrackFragment.h
@@ -96,6 +96,7 @@
 private:
     size_t mSampleIndex;
     size_t mSampleCount;
+    uint32_t mChunkIndex;
 
     SampleInfo mSampleInfo;
 
@@ -112,7 +113,6 @@
     uint64_t mNextSampleOffset;
 
     void updateSampleInfo();
-    void fixSampleToChunkTableIfNecessary();
 
     DISALLOW_EVIL_CONSTRUCTORS(StaticTrackFragment);
 };
diff --git a/services/camera/libcameraservice/Camera2Client.cpp b/services/camera/libcameraservice/Camera2Client.cpp
index cd74e6d..9c2fbcb 100644
--- a/services/camera/libcameraservice/Camera2Client.cpp
+++ b/services/camera/libcameraservice/Camera2Client.cpp
@@ -88,6 +88,8 @@
         return NO_INIT;
     }
 
+    res = mDevice->setNotifyCallback(this);
+
     res = buildDefaultParameters();
     if (res != OK) {
         ALOGE("%s: Camera %d: unable to build defaults: %s (%d)",
@@ -846,6 +848,15 @@
     status_t res;
     if ( (res = checkPid(__FUNCTION__) ) != OK) return res;
 
+    int triggerId;
+    {
+        LockedParameters::Key k(mParameters);
+        k.mParameters.currentAfTriggerId = ++k.mParameters.afTriggerCounter;
+        triggerId = k.mParameters.currentAfTriggerId;
+    }
+
+    mDevice->triggerAutofocus(triggerId);
+
     return OK;
 }
 
@@ -855,6 +866,14 @@
     status_t res;
     if ( (res = checkPid(__FUNCTION__) ) != OK) return res;
 
+    int triggerId;
+    {
+        LockedParameters::Key k(mParameters);
+        triggerId = ++k.mParameters.afTriggerCounter;
+    }
+
+    mDevice->triggerCancelAutofocus(triggerId);
+
     return OK;
 }
 
@@ -1453,6 +1472,9 @@
     k.mParameters.sceneMode = sceneMode;
 
     k.mParameters.flashMode = flashMode;
+    if (focusMode != k.mParameters.focusMode) {
+        k.mParameters.currentAfTriggerId = -1;
+    }
     k.mParameters.focusMode = focusMode;
 
     k.mParameters.focusingAreas = focusingAreas;
@@ -1625,7 +1647,9 @@
 }
 
 status_t Camera2Client::commandEnableFocusMoveMsgL(bool enable) {
-    ALOGE("%s: Unimplemented!", __FUNCTION__);
+    LockedParameters::Key k(mParameters);
+    k.mParameters.enableFocusMoveMessages = enable;
+
     return OK;
 }
 
@@ -1678,6 +1702,101 @@
 void Camera2Client::notifyAutoFocus(uint8_t newState, int triggerId) {
     ALOGV("%s: Autofocus state now %d, last trigger %d",
             __FUNCTION__, newState, triggerId);
+    bool sendCompletedMessage = false;
+    bool sendMovingMessage = false;
+
+    bool success = false;
+    bool afInMotion = false;
+    {
+        LockedParameters::Key k(mParameters);
+        switch (k.mParameters.focusMode) {
+            case Parameters::FOCUS_MODE_AUTO:
+            case Parameters::FOCUS_MODE_MACRO:
+                // Don't send notifications upstream if they're not for the current AF
+                // trigger. For example, if cancel was called in between, or if we
+                // already sent a notification about this AF call.
+                if (triggerId != k.mParameters.currentAfTriggerId) break;
+                switch (newState) {
+                    case ANDROID_CONTROL_AF_STATE_FOCUSED_LOCKED:
+                        success = true;
+                        // no break
+                    case ANDROID_CONTROL_AF_STATE_NOT_FOCUSED_LOCKED:
+                        sendCompletedMessage = true;
+                        k.mParameters.currentAfTriggerId = -1;
+                        break;
+                    case ANDROID_CONTROL_AF_STATE_ACTIVE_SCAN:
+                        // Just starting focusing, ignore
+                        break;
+                    case ANDROID_CONTROL_AF_STATE_INACTIVE:
+                    case ANDROID_CONTROL_AF_STATE_PASSIVE_SCAN:
+                    case ANDROID_CONTROL_AF_STATE_PASSIVE_FOCUSED:
+                    default:
+                        // Unexpected in AUTO/MACRO mode
+                        ALOGE("%s: Unexpected AF state transition in AUTO/MACRO mode: %d",
+                                __FUNCTION__, newState);
+                        break;
+                }
+                break;
+            case Parameters::FOCUS_MODE_CONTINUOUS_VIDEO:
+            case Parameters::FOCUS_MODE_CONTINUOUS_PICTURE:
+                switch (newState) {
+                    case ANDROID_CONTROL_AF_STATE_FOCUSED_LOCKED:
+                        success = true;
+                        // no break
+                    case ANDROID_CONTROL_AF_STATE_NOT_FOCUSED_LOCKED:
+                        // Don't send notifications upstream if they're not for
+                        // the current AF trigger. For example, if cancel was
+                        // called in between, or if we already sent a
+                        // notification about this AF call.
+                        // Send both a 'AF done' callback and a 'AF move' callback
+                        if (triggerId != k.mParameters.currentAfTriggerId) break;
+                        sendCompletedMessage = true;
+                        afInMotion = false;
+                        if (k.mParameters.enableFocusMoveMessages &&
+                                k.mParameters.afInMotion) {
+                            sendMovingMessage = true;
+                        }
+                        k.mParameters.currentAfTriggerId = -1;
+                        break;
+                    case ANDROID_CONTROL_AF_STATE_INACTIVE:
+                        // Cancel was called, or we switched state; care if
+                        // currently moving
+                        afInMotion = false;
+                        if (k.mParameters.enableFocusMoveMessages &&
+                                k.mParameters.afInMotion) {
+                            sendMovingMessage = true;
+                        }
+                        break;
+                    case ANDROID_CONTROL_AF_STATE_PASSIVE_SCAN:
+                        // Start passive scan, inform upstream
+                        afInMotion = true;
+                        // no break
+                    case ANDROID_CONTROL_AF_STATE_PASSIVE_FOCUSED:
+                        // Stop passive scan, inform upstream
+                        if (k.mParameters.enableFocusMoveMessages) {
+                            sendMovingMessage = true;
+                        }
+                        break;
+                }
+                k.mParameters.afInMotion = afInMotion;
+                break;
+            case Parameters::FOCUS_MODE_EDOF:
+            case Parameters::FOCUS_MODE_INFINITY:
+            case Parameters::FOCUS_MODE_FIXED:
+            default:
+                if (newState != ANDROID_CONTROL_AF_STATE_INACTIVE) {
+                    ALOGE("%s: Unexpected AF state change %d (ID %d) in focus mode %d",
+                          __FUNCTION__, newState, triggerId, k.mParameters.focusMode);
+                }
+        }
+    }
+    if (sendMovingMessage) {
+        mCameraClient->notifyCallback(CAMERA_MSG_FOCUS_MOVE,
+                afInMotion ? 1 : 0, 0);
+    }
+    if (sendCompletedMessage) {
+        mCameraClient->notifyCallback(CAMERA_MSG_FOCUS, success ? 1 : 0, 0);
+    }
 }
 
 void Camera2Client::notifyAutoExposure(uint8_t newState, int triggerId) {
@@ -2394,9 +2513,7 @@
         k.mParameters.focusMode = Parameters::FOCUS_MODE_AUTO;
         params.set(CameraParameters::KEY_FOCUS_MODE,
                 CameraParameters::FOCUS_MODE_AUTO);
-        String8 supportedFocusModes(CameraParameters::FOCUS_MODE_FIXED);
-        supportedFocusModes = supportedFocusModes + "," +
-            CameraParameters::FOCUS_MODE_INFINITY;
+        String8 supportedFocusModes(CameraParameters::FOCUS_MODE_INFINITY);
         bool addComma = true;
 
         for (size_t i=0; i < availableAfModes.count; i++) {
@@ -2573,6 +2690,8 @@
 
     k.mParameters.storeMetadataInBuffers = true;
     k.mParameters.playShutterSound = true;
+    k.mParameters.afTriggerCounter = 0;
+    k.mParameters.currentAfTriggerId = -1;
 
     k.mParameters.paramsFlattened = params.flatten();
 
diff --git a/services/camera/libcameraservice/Camera2Client.h b/services/camera/libcameraservice/Camera2Client.h
index 8c10d6b..dffd4ab 100644
--- a/services/camera/libcameraservice/Camera2Client.h
+++ b/services/camera/libcameraservice/Camera2Client.h
@@ -200,6 +200,11 @@
         // listed in Camera.Parameters
         bool storeMetadataInBuffers;
         bool playShutterSound;
+        bool enableFocusMoveMessages;
+
+        int afTriggerCounter;
+        int currentAfTriggerId;
+        bool afInMotion;
     };
 
     class LockedParameters {
@@ -313,9 +318,6 @@
     camera_metadata_t *mRecordingRequest;
     sp<Camera2Heap> mRecordingHeap;
 
-    // TODO: This needs to be queried from somewhere, or the BufferQueue needs
-    // to be passed all the way to stagefright. Right now, set to a large number
-    // to avoid starvation of the video encoders.
     static const size_t kDefaultRecordingHeapCount = 8;
     size_t mRecordingHeapCount;
     size_t mRecordingHeapHead, mRecordingHeapFree;
@@ -325,6 +327,10 @@
     status_t updateRecordingRequest(const Parameters &params);
     status_t updateRecordingStream(const Parameters &params);
 
+    /** Notification-related members */
+
+    bool mAfInMotion;
+
     /** Camera2Device instance wrapping HAL2 entry */
 
     sp<Camera2Device> mDevice;
diff --git a/services/camera/libcameraservice/Camera2Device.cpp b/services/camera/libcameraservice/Camera2Device.cpp
index 6eca7c0..7c97e1e 100644
--- a/services/camera/libcameraservice/Camera2Device.cpp
+++ b/services/camera/libcameraservice/Camera2Device.cpp
@@ -127,7 +127,8 @@
         }
     }
 
-    result.appendFormat("  Camera2Device[%d] dump (detail level %d):\n", mId);
+    result.appendFormat("  Camera2Device[%d] dump (detail level %d):\n",
+            mId, detailLevel);
 
     if (detailLevel > 0) {
         result = "    Request queue contents:\n";
@@ -339,6 +340,42 @@
     }
 }
 
+status_t Camera2Device::triggerAutofocus(uint32_t id) {
+    status_t res;
+    ALOGV("%s: Triggering autofocus, id %d", __FUNCTION__, id);
+    res = mDevice->ops->trigger_action(mDevice,
+            CAMERA2_TRIGGER_AUTOFOCUS, id, 0);
+    if (res != OK) {
+        ALOGE("%s: Error triggering autofocus (id %d)",
+                __FUNCTION__, id);
+    }
+    return res;
+}
+
+status_t Camera2Device::triggerCancelAutofocus(uint32_t id) {
+    status_t res;
+    ALOGV("%s: Canceling autofocus, id %d", __FUNCTION__, id);
+    res = mDevice->ops->trigger_action(mDevice,
+            CAMERA2_TRIGGER_CANCEL_AUTOFOCUS, id, 0);
+    if (res != OK) {
+        ALOGE("%s: Error canceling autofocus (id %d)",
+                __FUNCTION__, id);
+    }
+    return res;
+}
+
+status_t Camera2Device::triggerPrecaptureMetering(uint32_t id) {
+    status_t res;
+    ALOGV("%s: Triggering precapture metering, id %d", __FUNCTION__, id);
+    res = mDevice->ops->trigger_action(mDevice,
+            CAMERA2_TRIGGER_PRECAPTURE_METERING, id, 0);
+    if (res != OK) {
+        ALOGE("%s: Error triggering precapture metering (id %d)",
+                __FUNCTION__, id);
+    }
+    return res;
+}
+
 /**
  * Camera2Device::NotificationListener
  */
@@ -982,11 +1019,13 @@
     if (err != OK) {
         ALOGE("%s: Error queueing buffer to native window: %s (%d)",
                 __FUNCTION__, strerror(-err), err);
+        return err;
     }
+
     stream->mActiveBuffers--;
     stream->mFrameCount++;
     stream->mLastTimestamp = timestamp;
-    return err;
+    return OK;
 }
 
 int Camera2Device::StreamAdapter::cancel_buffer(const camera2_stream_ops_t* w,
@@ -999,10 +1038,18 @@
         ALOGE("%s: Called when in bad state: %d", __FUNCTION__, stream->mState);
         return INVALID_OPERATION;
     }
-    stream->mActiveBuffers--;
+
     ANativeWindow *a = toANW(w);
-    return a->cancelBuffer(a,
+    int err = a->cancelBuffer(a,
             container_of(buffer, ANativeWindowBuffer, handle), -1);
+    if (err != OK) {
+        ALOGE("%s: Error canceling buffer to native window: %s (%d)",
+                __FUNCTION__, strerror(-err), err);
+        return err;
+    }
+
+    stream->mActiveBuffers--;
+    return OK;
 }
 
 int Camera2Device::StreamAdapter::set_crop(const camera2_stream_ops_t* w,
diff --git a/services/camera/libcameraservice/Camera2Device.h b/services/camera/libcameraservice/Camera2Device.h
index 91a3fbd..9be370f 100644
--- a/services/camera/libcameraservice/Camera2Device.h
+++ b/services/camera/libcameraservice/Camera2Device.h
@@ -123,6 +123,27 @@
      */
     status_t setNotifyCallback(NotificationListener *listener);
 
+    /**
+     * Trigger auto-focus. The latest ID used in a trigger autofocus or cancel
+     * autofocus call will be returned by the HAL in all subsequent AF
+     * notifications.
+     */
+    status_t triggerAutofocus(uint32_t id);
+
+    /**
+     * Cancel auto-focus. The latest ID used in a trigger autofocus/cancel
+     * autofocus call will be returned by the HAL in all subsequent AF
+     * notifications.
+     */
+    status_t triggerCancelAutofocus(uint32_t id);
+
+    /**
+     * Trigger pre-capture metering. The latest ID used in a trigger pre-capture
+     * call will be returned by the HAL in all subsequent AE and AWB
+     * notifications.
+     */
+    status_t triggerPrecaptureMetering(uint32_t id);
+
   private:
 
     const int mId;