Merge "Added an additional virtual method IStreamSource::flags()." into jb-mr1-dev
diff --git a/include/media/AudioTrack.h b/include/media/AudioTrack.h
index 26a25b0..34108b3 100644
--- a/include/media/AudioTrack.h
+++ b/include/media/AudioTrack.h
@@ -547,7 +547,7 @@
     status_t allocateTimedBuffer(size_t size, sp<IMemory>* buffer);
 
     /* queue a buffer obtained via allocateTimedBuffer for playback at the
-       given timestamp.  PTS units a microseconds on the media time timeline.
+       given timestamp.  PTS units are microseconds on the media time timeline.
        The media time transform (set with setMediaTimeTransform) set by the
        audio producer will handle converting from media time to local time
        (perhaps going through the common time timeline in the case of
diff --git a/media/libmedia/AudioTrack.cpp b/media/libmedia/AudioTrack.cpp
index 73d396e..362d022 100644
--- a/media/libmedia/AudioTrack.cpp
+++ b/media/libmedia/AudioTrack.cpp
@@ -1144,7 +1144,7 @@
 
     // If the track is not invalid already, try to allocate a buffer.  alloc
     // fails indicating that the server is dead, flag the track as invalid so
-    // we can attempt to restore in in just a bit.
+    // we can attempt to restore in just a bit.
     if (!(mCblk->flags & CBLK_INVALID_MSK)) {
         result = mAudioTrack->allocateTimedBuffer(size, buffer);
         if (result == DEAD_OBJECT) {
diff --git a/media/libmediaplayerservice/nuplayer/RTSPSource.cpp b/media/libmediaplayerservice/nuplayer/RTSPSource.cpp
index 4a704e3..5a7a785 100644
--- a/media/libmediaplayerservice/nuplayer/RTSPSource.cpp
+++ b/media/libmediaplayerservice/nuplayer/RTSPSource.cpp
@@ -23,6 +23,7 @@
 #include "AnotherPacketSource.h"
 #include "MyHandler.h"
 
+#include <media/stagefright/MediaDefs.h>
 #include <media/stagefright/MetaData.h>
 
 namespace android {
@@ -159,6 +160,13 @@
 }
 
 sp<AnotherPacketSource> NuPlayer::RTSPSource::getSource(bool audio) {
+    if (mTSParser != NULL) {
+        sp<MediaSource> source = mTSParser->getSource(
+                audio ? ATSParser::AUDIO : ATSParser::VIDEO);
+
+        return static_cast<AnotherPacketSource *>(source.get());
+    }
+
     return audio ? mAudioTrack : mVideoTrack;
 }
 
@@ -255,7 +263,12 @@
         {
             size_t trackIndex;
             CHECK(msg->findSize("trackIndex", &trackIndex));
-            CHECK_LT(trackIndex, mTracks.size());
+
+            if (mTSParser == NULL) {
+                CHECK_LT(trackIndex, mTracks.size());
+            } else {
+                CHECK_EQ(trackIndex, 0u);
+            }
 
             sp<ABuffer> accessUnit;
             CHECK(msg->findBuffer("accessUnit", &accessUnit));
@@ -267,6 +280,37 @@
                 break;
             }
 
+            if (mTSParser != NULL) {
+                size_t offset = 0;
+                status_t err = OK;
+                while (offset + 188 <= accessUnit->size()) {
+                    err = mTSParser->feedTSPacket(
+                            accessUnit->data() + offset, 188);
+                    if (err != OK) {
+                        break;
+                    }
+
+                    offset += 188;
+                }
+
+                if (offset < accessUnit->size()) {
+                    err = ERROR_MALFORMED;
+                }
+
+                if (err != OK) {
+                    sp<AnotherPacketSource> source = getSource(false /* audio */);
+                    if (source != NULL) {
+                        source->signalEOS(err);
+                    }
+
+                    source = getSource(true /* audio */);
+                    if (source != NULL) {
+                        source->signalEOS(err);
+                    }
+                }
+                break;
+            }
+
             TrackInfo *info = &mTracks.editItemAt(trackIndex);
 
             sp<AnotherPacketSource> source = info->mSource;
@@ -296,14 +340,28 @@
 
         case MyHandler::kWhatEOS:
         {
-            size_t trackIndex;
-            CHECK(msg->findSize("trackIndex", &trackIndex));
-            CHECK_LT(trackIndex, mTracks.size());
-
             int32_t finalResult;
             CHECK(msg->findInt32("finalResult", &finalResult));
             CHECK_NE(finalResult, (status_t)OK);
 
+            if (mTSParser != NULL) {
+                sp<AnotherPacketSource> source = getSource(false /* audio */);
+                if (source != NULL) {
+                    source->signalEOS(finalResult);
+                }
+
+                source = getSource(true /* audio */);
+                if (source != NULL) {
+                    source->signalEOS(finalResult);
+                }
+
+                return;
+            }
+
+            size_t trackIndex;
+            CHECK(msg->findSize("trackIndex", &trackIndex));
+            CHECK_LT(trackIndex, mTracks.size());
+
             TrackInfo *info = &mTracks.editItemAt(trackIndex);
             sp<AnotherPacketSource> source = info->mSource;
             if (source != NULL) {
@@ -364,6 +422,14 @@
         const char *mime;
         CHECK(format->findCString(kKeyMIMEType, &mime));
 
+        if (!strcasecmp(mime, MEDIA_MIMETYPE_CONTAINER_MPEG2TS)) {
+            // Very special case for MPEG2 Transport Streams.
+            CHECK_EQ(numTracks, 1u);
+
+            mTSParser = new ATSParser;
+            return;
+        }
+
         bool isAudio = !strncasecmp(mime, "audio/", 6);
         bool isVideo = !strncasecmp(mime, "video/", 6);
 
diff --git a/media/libmediaplayerservice/nuplayer/RTSPSource.h b/media/libmediaplayerservice/nuplayer/RTSPSource.h
index c8409e5..f07c724 100644
--- a/media/libmediaplayerservice/nuplayer/RTSPSource.h
+++ b/media/libmediaplayerservice/nuplayer/RTSPSource.h
@@ -20,6 +20,8 @@
 
 #include "NuPlayerSource.h"
 
+#include "ATSParser.h"
+
 #include <media/stagefright/foundation/AHandlerReflector.h>
 
 namespace android {
@@ -99,6 +101,8 @@
     sp<AnotherPacketSource> mAudioTrack;
     sp<AnotherPacketSource> mVideoTrack;
 
+    sp<ATSParser> mTSParser;
+
     int32_t mSeekGeneration;
 
     sp<AnotherPacketSource> getSource(bool audio);
diff --git a/media/libnbaio/README.txt b/media/libnbaio/README.txt
new file mode 100644
index 0000000..8d74ab2
--- /dev/null
+++ b/media/libnbaio/README.txt
@@ -0,0 +1,40 @@
+libnbaio (for "Non-Blocking Audio I/O") was originally intended to
+be a purely non-blocking API.  It has evolved to now include
+a few blocking implementations of the interface.
+
+Note: as used here, "short transfer count" means the return value for
+read() or write() that indicates the actual number of successfully
+transferred frames is less than the requested number of frames.
+
+Pipe
+----
+supports 1 writer and N readers
+
+no mutexes, so safe to use between SCHED_NORMAL and SCHED_FIFO threads
+
+writes:
+  non-blocking
+  never return a short transfer count
+  overwrite data if not consumed quickly enough
+
+reads:
+  non-blocking
+  return a short transfer count if not enough data
+  will lose data if reader doesn't keep up
+
+MonoPipe
+--------
+supports 1 writer and 1 reader
+
+no mutexes, so safe to use between SCHED_NORMAL and SCHED_FIFO threads
+
+write are optionally blocking:
+  if configured to block, then will wait until space available before returning
+  if configured to not block, then will return a short transfer count
+    and will never overwrite data
+
+reads:
+  non-blocking
+  return a short transfer count if not enough data
+  never lose data
+
diff --git a/media/libstagefright/mpeg2ts/ESQueue.cpp b/media/libstagefright/mpeg2ts/ESQueue.cpp
index 1cab077..e58e9bf 100644
--- a/media/libstagefright/mpeg2ts/ESQueue.cpp
+++ b/media/libstagefright/mpeg2ts/ESQueue.cpp
@@ -33,8 +33,9 @@
 
 namespace android {
 
-ElementaryStreamQueue::ElementaryStreamQueue(Mode mode)
-    : mMode(mode) {
+ElementaryStreamQueue::ElementaryStreamQueue(Mode mode, uint32_t flags)
+    : mMode(mode),
+      mFlags(flags) {
 }
 
 sp<MetaData> ElementaryStreamQueue::getFormat() {
@@ -289,6 +290,31 @@
 }
 
 sp<ABuffer> ElementaryStreamQueue::dequeueAccessUnit() {
+    if ((mFlags & kFlag_AlignedData) && mMode == H264) {
+        if (mRangeInfos.empty()) {
+            return NULL;
+        }
+
+        RangeInfo info = *mRangeInfos.begin();
+        mRangeInfos.erase(mRangeInfos.begin());
+
+        sp<ABuffer> accessUnit = new ABuffer(info.mLength);
+        memcpy(accessUnit->data(), mBuffer->data(), info.mLength);
+        accessUnit->meta()->setInt64("timeUs", info.mTimestampUs);
+
+        memmove(mBuffer->data(),
+                mBuffer->data() + info.mLength,
+                mBuffer->size() - info.mLength);
+
+        mBuffer->setRange(0, mBuffer->size() - info.mLength);
+
+        if (mFormat == NULL) {
+            mFormat = MakeAVCCodecSpecificData(accessUnit);
+        }
+
+        return accessUnit;
+    }
+
     switch (mMode) {
         case H264:
             return dequeueAccessUnitH264();
@@ -436,8 +462,8 @@
 
 sp<ABuffer> ElementaryStreamQueue::dequeueAccessUnitH264() {
     const uint8_t *data = mBuffer->data();
-    size_t size = mBuffer->size();
 
+    size_t size = mBuffer->size();
     Vector<NALPosition> nals;
 
     size_t totalSize = 0;
diff --git a/media/libstagefright/mpeg2ts/ESQueue.h b/media/libstagefright/mpeg2ts/ESQueue.h
index 4035ed3..72aa2e7 100644
--- a/media/libstagefright/mpeg2ts/ESQueue.h
+++ b/media/libstagefright/mpeg2ts/ESQueue.h
@@ -36,7 +36,12 @@
         MPEG_VIDEO,
         MPEG4_VIDEO,
     };
-    ElementaryStreamQueue(Mode mode);
+
+    enum Flags {
+        // Data appended to the queue is always at access unit boundaries.
+        kFlag_AlignedData = 1,
+    };
+    ElementaryStreamQueue(Mode mode, uint32_t flags = 0);
 
     status_t appendData(const void *data, size_t size, int64_t timeUs);
     void clear(bool clearFormat);
@@ -52,6 +57,7 @@
     };
 
     Mode mMode;
+    uint32_t mFlags;
 
     sp<ABuffer> mBuffer;
     List<RangeInfo> mRangeInfos;
diff --git a/media/libstagefright/rtsp/AMPEG2TSAssembler.cpp b/media/libstagefright/rtsp/AMPEG2TSAssembler.cpp
new file mode 100644
index 0000000..4c9bf5b
--- /dev/null
+++ b/media/libstagefright/rtsp/AMPEG2TSAssembler.cpp
@@ -0,0 +1,119 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+//#define LOG_NDEBUG 0
+#define LOG_TAG "AMPEG2TSAssembler"
+#include <utils/Log.h>
+
+#include "AMPEG2TSAssembler.h"
+
+#include "ARTPSource.h"
+#include "ASessionDescription.h"
+
+#include <media/stagefright/foundation/ABuffer.h>
+#include <media/stagefright/foundation/ADebug.h>
+#include <media/stagefright/foundation/AMessage.h>
+#include <media/stagefright/foundation/hexdump.h>
+#include <media/stagefright/MediaDefs.h>
+#include <media/stagefright/MetaData.h>
+#include <media/stagefright/Utils.h>
+
+namespace android {
+
+AMPEG2TSAssembler::AMPEG2TSAssembler(
+        const sp<AMessage> &notify, const char *desc, const AString &params)
+    : mNotifyMsg(notify),
+      mNextExpectedSeqNoValid(false),
+      mNextExpectedSeqNo(0) {
+}
+
+AMPEG2TSAssembler::~AMPEG2TSAssembler() {
+}
+
+ARTPAssembler::AssemblyStatus AMPEG2TSAssembler::assembleMore(
+        const sp<ARTPSource> &source) {
+    return addPacket(source);
+}
+
+ARTPAssembler::AssemblyStatus AMPEG2TSAssembler::addPacket(
+        const sp<ARTPSource> &source) {
+    List<sp<ABuffer> > *queue = source->queue();
+
+    if (queue->empty()) {
+        return NOT_ENOUGH_DATA;
+    }
+
+    if (mNextExpectedSeqNoValid) {
+        List<sp<ABuffer> >::iterator it = queue->begin();
+        while (it != queue->end()) {
+            if ((uint32_t)(*it)->int32Data() >= mNextExpectedSeqNo) {
+                break;
+            }
+
+            it = queue->erase(it);
+        }
+
+        if (queue->empty()) {
+            return NOT_ENOUGH_DATA;
+        }
+    }
+
+    sp<ABuffer> buffer = *queue->begin();
+
+    if (!mNextExpectedSeqNoValid) {
+        mNextExpectedSeqNoValid = true;
+        mNextExpectedSeqNo = (uint32_t)buffer->int32Data();
+    } else if ((uint32_t)buffer->int32Data() != mNextExpectedSeqNo) {
+        ALOGV("Not the sequence number I expected");
+
+        return WRONG_SEQUENCE_NUMBER;
+    }
+
+    // hexdump(buffer->data(), buffer->size());
+
+    if ((buffer->size() % 188) > 0) {
+        queue->erase(queue->begin());
+        ++mNextExpectedSeqNo;
+
+        ALOGV("Not a multiple of transport packet size.");
+
+        return MALFORMED_PACKET;
+    }
+
+    sp<AMessage> msg = mNotifyMsg->dup();
+    msg->setBuffer("access-unit", buffer);
+    msg->post();
+
+    queue->erase(queue->begin());
+    ++mNextExpectedSeqNo;
+
+    return OK;
+}
+
+void AMPEG2TSAssembler::packetLost() {
+    CHECK(mNextExpectedSeqNoValid);
+    ++mNextExpectedSeqNo;
+}
+
+void AMPEG2TSAssembler::onByeReceived() {
+    sp<AMessage> msg = mNotifyMsg->dup();
+    msg->setInt32("eos", true);
+    msg->post();
+}
+
+}  // namespace android
+
+
diff --git a/media/libstagefright/rtsp/AMPEG2TSAssembler.h b/media/libstagefright/rtsp/AMPEG2TSAssembler.h
new file mode 100644
index 0000000..712e18e
--- /dev/null
+++ b/media/libstagefright/rtsp/AMPEG2TSAssembler.h
@@ -0,0 +1,54 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef A_MPEG2_TS_ASSEMBLER_H_
+
+#define A_MPEG2_TS_ASSEMBLER_H_
+
+#include "ARTPAssembler.h"
+
+namespace android {
+
+struct AMessage;
+struct AString;
+struct MetaData;
+
+struct AMPEG2TSAssembler : public ARTPAssembler {
+    AMPEG2TSAssembler(
+            const sp<AMessage> &notify,
+            const char *desc, const AString &params);
+
+protected:
+    virtual ~AMPEG2TSAssembler();
+
+    virtual AssemblyStatus assembleMore(const sp<ARTPSource> &source);
+    virtual void onByeReceived();
+    virtual void packetLost();
+
+private:
+    sp<AMessage> mNotifyMsg;
+    bool mNextExpectedSeqNoValid;
+    uint32_t mNextExpectedSeqNo;
+
+    AssemblyStatus addPacket(const sp<ARTPSource> &source);
+
+    DISALLOW_EVIL_CONSTRUCTORS(AMPEG2TSAssembler);
+};
+
+}  // namespace android
+
+#endif  // A_MPEG2_TS_ASSEMBLER_H_
+
diff --git a/media/libstagefright/rtsp/APacketSource.cpp b/media/libstagefright/rtsp/APacketSource.cpp
index ddd2f06..462c384 100644
--- a/media/libstagefright/rtsp/APacketSource.cpp
+++ b/media/libstagefright/rtsp/APacketSource.cpp
@@ -566,6 +566,8 @@
                 codecSpecificData->data(), codecSpecificData->size());
     } else if (ARawAudioAssembler::Supports(desc.c_str())) {
         ARawAudioAssembler::MakeFormat(desc.c_str(), mFormat);
+    } else if (!strncasecmp("MP2T/", desc.c_str(), 5)) {
+        mFormat->setCString(kKeyMIMEType, MEDIA_MIMETYPE_CONTAINER_MPEG2TS);
     } else {
         mInitCheck = ERROR_UNSUPPORTED;
     }
diff --git a/media/libstagefright/rtsp/ARTPSource.cpp b/media/libstagefright/rtsp/ARTPSource.cpp
index ed68790..d7c3bd6 100644
--- a/media/libstagefright/rtsp/ARTPSource.cpp
+++ b/media/libstagefright/rtsp/ARTPSource.cpp
@@ -23,6 +23,7 @@
 #include "AAMRAssembler.h"
 #include "AAVCAssembler.h"
 #include "AH263Assembler.h"
+#include "AMPEG2TSAssembler.h"
 #include "AMPEG4AudioAssembler.h"
 #include "AMPEG4ElementaryAssembler.h"
 #include "ARawAudioAssembler.h"
@@ -73,6 +74,8 @@
         mIssueFIRRequests = true;
     } else if (ARawAudioAssembler::Supports(desc.c_str())) {
         mAssembler = new ARawAudioAssembler(notify, desc.c_str(), params);
+    } else if (!strncasecmp(desc.c_str(), "MP2T/", 5)) {
+        mAssembler = new AMPEG2TSAssembler(notify, desc.c_str(), params);
     } else {
         TRESPASS();
     }
diff --git a/media/libstagefright/rtsp/Android.mk b/media/libstagefright/rtsp/Android.mk
index d0f5259..49e2daf 100644
--- a/media/libstagefright/rtsp/Android.mk
+++ b/media/libstagefright/rtsp/Android.mk
@@ -6,6 +6,7 @@
         AAMRAssembler.cpp           \
         AAVCAssembler.cpp           \
         AH263Assembler.cpp          \
+        AMPEG2TSAssembler.cpp       \
         AMPEG4AudioAssembler.cpp    \
         AMPEG4ElementaryAssembler.cpp \
         APacketSource.cpp           \
diff --git a/services/audioflinger/AudioFlinger.cpp b/services/audioflinger/AudioFlinger.cpp
index 1a37f4f..14f74b5 100644
--- a/services/audioflinger/AudioFlinger.cpp
+++ b/services/audioflinger/AudioFlinger.cpp
@@ -169,8 +169,8 @@
 // for the track.  The client then sub-divides this into smaller buffers for its use.
 // Currently the client uses double-buffering by default, but doesn't tell us about that.
 // So for now we just assume that client is double-buffered.
-// FIXME It would be better for client to tell us whether it wants double-buffering or N-buffering,
-// so we could allocate the right amount of memory.
+// FIXME It would be better for client to tell AudioFlinger whether it wants double-buffering or
+// N-buffering, so AudioFlinger could allocate the right amount of memory.
 // See the client's minBufCount and mNotificationFramesAct calculations for details.
 static const int kFastTrackMultiplier = 2;
 
@@ -258,11 +258,11 @@
 AudioFlinger::~AudioFlinger()
 {
     while (!mRecordThreads.isEmpty()) {
-        // closeInput() will remove first entry from mRecordThreads
+        // closeInput_nonvirtual() will remove specified entry from mRecordThreads
         closeInput_nonvirtual(mRecordThreads.keyAt(0));
     }
     while (!mPlaybackThreads.isEmpty()) {
-        // closeOutput() will remove first entry from mPlaybackThreads
+        // closeOutput_nonvirtual() will remove specified entry from mPlaybackThreads
         closeOutput_nonvirtual(mPlaybackThreads.keyAt(0));
     }
 
@@ -1134,7 +1134,7 @@
 
 AudioFlinger::ThreadBase::ThreadBase(const sp<AudioFlinger>& audioFlinger, audio_io_handle_t id,
         audio_devices_t device, type_t type)
-    :   Thread(false),
+    :   Thread(false /*canCallJava*/),
         mType(type),
         mAudioFlinger(audioFlinger), mSampleRate(0), mFrameCount(0), mNormalFrameCount(0),
         // mChannelMask
@@ -1142,6 +1142,7 @@
         mFrameSize(1), mFormat(AUDIO_FORMAT_INVALID),
         mParamStatus(NO_ERROR),
         mStandby(false), mDevice(device), mId(id),
+        // mName will be set by concrete (non-virtual) subclass
         mDeathRecipient(new PMDeathRecipient(this))
 {
 }
@@ -6097,7 +6098,7 @@
                     if (mChannelCount == 1 && mReqChannelCount == 1) {
                         framesOut >>= 1;
                     }
-                    mResampler->resample(mRsmpOutBuffer, framesOut, this);
+                    mResampler->resample(mRsmpOutBuffer, framesOut, this /* AudioBufferProvider* */);
                     // ditherAndClamp() works as long as all buffers returned by mActiveTrack->getNextBuffer()
                     // are 32 bit aligned which should be always true.
                     if (mChannelCount == 2 && mReqChannelCount == 1) {
diff --git a/services/audioflinger/AudioFlinger.h b/services/audioflinger/AudioFlinger.h
index b4aefc1..4723cd9 100644
--- a/services/audioflinger/AudioFlinger.h
+++ b/services/audioflinger/AudioFlinger.h
@@ -454,8 +454,9 @@
             /*const*/ sp<Client> mClient;   // see explanation at ~TrackBase() why not const
             sp<IMemory>         mCblkMemory;
             audio_track_cblk_t* mCblk;
-            void*               mBuffer;
-            void*               mBufferEnd;
+            void*               mBuffer;    // start of track buffer, typically in shared memory
+            void*               mBufferEnd; // &mBuffer[mFrameCount * frameSize], where frameSize
+                                            //   is based on mChannelCount and 16-bit samples
             uint32_t            mFrameCount;
             // we don't really need a lock for these
             track_state         mState;
@@ -1364,6 +1365,7 @@
 
     // record thread
     class RecordThread : public ThreadBase, public AudioBufferProvider
+                            // derives from AudioBufferProvider interface for use by resampler
     {
     public:
 
@@ -1420,7 +1422,7 @@
         void        dumpInternals(int fd, const Vector<String16>& args);
         void        dumpTracks(int fd, const Vector<String16>& args);
 
-        // Thread
+        // Thread virtuals
         virtual bool        threadLoop();
         virtual status_t    readyToRun();
 
@@ -1968,9 +1970,10 @@
                 DefaultKeyedVector< audio_io_handle_t, sp<PlaybackThread> >  mPlaybackThreads;
                 stream_type_t                       mStreamTypes[AUDIO_STREAM_CNT];
 
-                // both are protected by mLock
+                // member variables below are protected by mLock
                 float                               mMasterVolume;
                 bool                                mMasterMute;
+                // end of variables protected by mLock
 
                 DefaultKeyedVector< audio_io_handle_t, sp<RecordThread> >    mRecordThreads;
 
diff --git a/services/camera/libcameraservice/Camera2Device.cpp b/services/camera/libcameraservice/Camera2Device.cpp
index 7cac025..f62c0a0 100644
--- a/services/camera/libcameraservice/Camera2Device.cpp
+++ b/services/camera/libcameraservice/Camera2Device.cpp
@@ -109,7 +109,12 @@
                 __FUNCTION__, mId, strerror(-res), res);
         return res;
     }
-
+    res = set_camera_metadata_vendor_tag_ops(mVendorTagOps);
+    if (res != OK) {
+        ALOGE("%s: Camera %d: Unable to set tag ops: %s (%d)",
+            __FUNCTION__, mId, strerror(-res), res);
+        return res;
+    }
     setNotifyCallback(NULL);
 
     return OK;