Camera3Device: Add dumpsys monitoring of request/result metadata

Add new -m dumpsys option to cameraservice dump for monitoring
changes in selected metadata values in requests and results.

This option takes a comma-separated list of metadata keys, or the
shortcut value "3a", which expands to all the "android.control" tags.

In subsequent dumpsys calls, the last 100 changes to the tags being
monitored are listed.

The monitoring must be turned on once the camera device is running.

Bug:
Change-Id: If8938b30611ccafa86c2c4a06e57fc72680f827b
diff --git a/camera/CameraMetadata.cpp b/camera/CameraMetadata.cpp
index c78fc5d..373b94e 100644
--- a/camera/CameraMetadata.cpp
+++ b/camera/CameraMetadata.cpp
@@ -20,8 +20,9 @@
 #include <utils/Log.h>
 #include <utils/Errors.h>
 
-#include <camera/CameraMetadata.h>
 #include <binder/Parcel.h>
+#include <camera/CameraMetadata.h>
+#include <camera/VendorTagDescriptor.h>
 
 namespace android {
 
@@ -277,6 +278,18 @@
     return updateImpl(tag, (const void*)string.string(), string.size() + 1);
 }
 
+status_t CameraMetadata::update(const camera_metadata_ro_entry &entry) {
+    status_t res;
+    if (mLocked) {
+        ALOGE("%s: CameraMetadata is locked", __FUNCTION__);
+        return INVALID_OPERATION;
+    }
+    if ( (res = checkType(entry.tag, entry.type)) != OK) {
+        return res;
+    }
+    return updateImpl(entry.tag, (const void*)entry.data.u8, entry.count);
+}
+
 status_t CameraMetadata::updateImpl(uint32_t tag, const void *data,
         size_t data_count) {
     status_t res;
@@ -681,4 +694,99 @@
     mBuffer = otherBuf;
 }
 
+status_t CameraMetadata::getTagFromName(const char *name,
+        const VendorTagDescriptor* vTags, uint32_t *tag) {
+
+    if (name == nullptr || tag == nullptr) return BAD_VALUE;
+
+    size_t nameLength = strlen(name);
+
+    const SortedVector<String8> *vendorSections;
+    size_t vendorSectionCount = 0;
+
+    if (vTags != NULL) {
+        vendorSections = vTags->getAllSectionNames();
+        vendorSectionCount = vendorSections->size();
+    }
+
+    // First, find the section by the longest string match
+    const char *section = NULL;
+    size_t sectionIndex = 0;
+    size_t sectionLength = 0;
+    size_t totalSectionCount = ANDROID_SECTION_COUNT + vendorSectionCount;
+    for (size_t i = 0; i < totalSectionCount; ++i) {
+
+        const char *str = (i < ANDROID_SECTION_COUNT) ? camera_metadata_section_names[i] :
+                (*vendorSections)[i - ANDROID_SECTION_COUNT].string();
+
+        ALOGV("%s: Trying to match against section '%s'", __FUNCTION__, str);
+
+        if (strstr(name, str) == name) { // name begins with the section name
+            size_t strLength = strlen(str);
+
+            ALOGV("%s: Name begins with section name", __FUNCTION__);
+
+            // section name is the longest we've found so far
+            if (section == NULL || sectionLength < strLength) {
+                section = str;
+                sectionIndex = i;
+                sectionLength = strLength;
+
+                ALOGV("%s: Found new best section (%s)", __FUNCTION__, section);
+            }
+        }
+    }
+
+    // TODO: Make above get_camera_metadata_section_from_name ?
+
+    if (section == NULL) {
+        return NAME_NOT_FOUND;
+    } else {
+        ALOGV("%s: Found matched section '%s' (%zu)",
+              __FUNCTION__, section, sectionIndex);
+    }
+
+    // Get the tag name component of the name
+    const char *nameTagName = name + sectionLength + 1; // x.y.z -> z
+    if (sectionLength + 1 >= nameLength) {
+        return BAD_VALUE;
+    }
+
+    // Match rest of name against the tag names in that section only
+    uint32_t candidateTag = 0;
+    if (sectionIndex < ANDROID_SECTION_COUNT) {
+        // Match built-in tags (typically android.*)
+        uint32_t tagBegin, tagEnd; // [tagBegin, tagEnd)
+        tagBegin = camera_metadata_section_bounds[sectionIndex][0];
+        tagEnd = camera_metadata_section_bounds[sectionIndex][1];
+
+        for (candidateTag = tagBegin; candidateTag < tagEnd; ++candidateTag) {
+            const char *tagName = get_camera_metadata_tag_name(candidateTag);
+
+            if (strcmp(nameTagName, tagName) == 0) {
+                ALOGV("%s: Found matched tag '%s' (%d)",
+                      __FUNCTION__, tagName, candidateTag);
+                break;
+            }
+        }
+
+        if (candidateTag == tagEnd) {
+            return NAME_NOT_FOUND;
+        }
+    } else if (vTags != NULL) {
+        // Match vendor tags (typically com.*)
+        const String8 sectionName(section);
+        const String8 tagName(nameTagName);
+
+        status_t res = OK;
+        if ((res = vTags->lookupTag(tagName, sectionName, &candidateTag)) != OK) {
+            return NAME_NOT_FOUND;
+        }
+    }
+
+    *tag = candidateTag;
+    return OK;
+}
+
+
 }; // namespace android
diff --git a/camera/VendorTagDescriptor.cpp b/camera/VendorTagDescriptor.cpp
index 5538da9..02ece14 100644
--- a/camera/VendorTagDescriptor.cpp
+++ b/camera/VendorTagDescriptor.cpp
@@ -280,8 +280,8 @@
     return res;
 }
 
-SortedVector<String8> VendorTagDescriptor::getAllSectionNames() const {
-    return mSections;
+const SortedVector<String8>* VendorTagDescriptor::getAllSectionNames() const {
+    return &mSections;
 }
 
 status_t VendorTagDescriptor::lookupTag(String8 name, String8 section, /*out*/uint32_t* tag) const {
diff --git a/include/camera/CameraMetadata.h b/include/camera/CameraMetadata.h
index 28f47a1..d284477 100644
--- a/include/camera/CameraMetadata.h
+++ b/include/camera/CameraMetadata.h
@@ -18,12 +18,15 @@
 #define ANDROID_CLIENT_CAMERA2_CAMERAMETADATA_CPP
 
 #include "system/camera_metadata.h"
+
 #include <utils/String8.h>
 #include <utils/Vector.h>
 #include <binder/Parcelable.h>
 
 namespace android {
 
+class VendorTagDescriptor;
+
 /**
  * A convenience wrapper around the C-based camera_metadata_t library.
  */
@@ -137,6 +140,8 @@
             const camera_metadata_rational_t *data, size_t data_count);
     status_t update(uint32_t tag,
             const String8 &string);
+    status_t update(const camera_metadata_ro_entry &entry);
+
 
     template<typename T>
     status_t update(uint32_t tag, Vector<T> data) {
@@ -206,6 +211,15 @@
     static status_t writeToParcel(Parcel &parcel,
                                   const camera_metadata_t* metadata);
 
+    /**
+     * Find tag id for a given tag name, also checking vendor tags if available.
+     * On success, returns OK and writes the tag id into tag.
+     *
+     * This is a slow method.
+     */
+    static status_t getTagFromName(const char *name,
+            const VendorTagDescriptor* vTags, uint32_t *tag);
+
   private:
     camera_metadata_t *mBuffer;
     mutable bool       mLocked;
diff --git a/include/camera/VendorTagDescriptor.h b/include/camera/VendorTagDescriptor.h
index 4c1cab6..60e2d2d 100644
--- a/include/camera/VendorTagDescriptor.h
+++ b/include/camera/VendorTagDescriptor.h
@@ -81,8 +81,10 @@
         /**
          * Convenience method to get a vector containing all vendor tag
          * sections, or an empty vector if none are defined.
+         * The pointer is valid for the lifetime of the VendorTagDescriptor,
+         * or until readParcel or copyFrom is invoked.
          */
-        SortedVector<String8> getAllSectionNames() const;
+        const SortedVector<String8>* getAllSectionNames() const;
 
         /**
          * Lookup the tag id for a given tag name and section.
diff --git a/services/camera/libcameraservice/Android.mk b/services/camera/libcameraservice/Android.mk
index ebe65e4..8d7f71c 100644
--- a/services/camera/libcameraservice/Android.mk
+++ b/services/camera/libcameraservice/Android.mk
@@ -51,7 +51,8 @@
     device3/Camera3BufferManager.cpp \
     gui/RingBufferConsumer.cpp \
     utils/CameraTraces.cpp \
-    utils/AutoConditionLock.cpp
+    utils/AutoConditionLock.cpp \
+    utils/TagMonitor.cpp
 
 LOCAL_SHARED_LIBRARIES:= \
     libui \
diff --git a/services/camera/libcameraservice/device3/Camera3Device.cpp b/services/camera/libcameraservice/device3/Camera3Device.cpp
index bbe7317..3b51239 100644
--- a/services/camera/libcameraservice/device3/Camera3Device.cpp
+++ b/services/camera/libcameraservice/device3/Camera3Device.cpp
@@ -530,12 +530,26 @@
             mId, __FUNCTION__);
 
     bool dumpTemplates = false;
+
     String16 templatesOption("-t");
+    String16 monitorOption("-m");
     int n = args.size();
     for (int i = 0; i < n; i++) {
         if (args[i] == templatesOption) {
             dumpTemplates = true;
         }
+        if (args[i] == monitorOption) {
+            if (i + 1 < n) {
+                String8 monitorTags = String8(args[i + 1]);
+                if (monitorTags == "off") {
+                    mTagMonitor.disableMonitoring();
+                } else {
+                    mTagMonitor.parseTagsToMonitor(monitorTags);
+                }
+            } else {
+                mTagMonitor.disableMonitoring();
+            }
+        }
     }
 
     String8 lines;
@@ -622,6 +636,8 @@
         }
     }
 
+    mTagMonitor.dumpMonitoredMetadata(fd);
+
     if (mHal3Device != NULL) {
         lines = String8("    HAL device dump:\n");
         write(fd, lines.string(), lines.size());
@@ -2346,13 +2362,16 @@
     captureResult.mMetadata.sort();
 
     // Check that there's a timestamp in the result metadata
-    camera_metadata_entry entry = captureResult.mMetadata.find(ANDROID_SENSOR_TIMESTAMP);
-    if (entry.count == 0) {
+    camera_metadata_entry timestamp = captureResult.mMetadata.find(ANDROID_SENSOR_TIMESTAMP);
+    if (timestamp.count == 0) {
         SET_ERR("No timestamp provided by HAL for frame %d!",
                 frameNumber);
         return;
     }
 
+    mTagMonitor.monitorMetadata(TagMonitor::RESULT,
+            frameNumber, timestamp.data.i64[0], captureResult.mMetadata);
+
     insertResultLocked(&captureResult, frameNumber, aeTriggerCancelOverride);
 }
 
@@ -2721,6 +2740,11 @@
 }
 
 
+void Camera3Device::monitorMetadata(TagMonitor::eventSource source,
+        int64_t frameNumber, nsecs_t timestamp, const CameraMetadata& metadata) {
+    mTagMonitor.monitorMetadata(source, frameNumber, timestamp, metadata);
+}
+
 /**
  * RequestThread inner class methods
  */
@@ -3147,6 +3171,12 @@
 
             camera_metadata_t* cloned = clone_camera_metadata(nextRequest.halRequest.settings);
             mLatestRequest.acquire(cloned);
+
+            sp<Camera3Device> parent = mParent.promote();
+            if (parent != NULL) {
+                parent->monitorMetadata(TagMonitor::REQUEST, nextRequest.halRequest.frame_number,
+                        0, mLatestRequest);
+            }
         }
 
         if (nextRequest.halRequest.settings != NULL) {
diff --git a/services/camera/libcameraservice/device3/Camera3Device.h b/services/camera/libcameraservice/device3/Camera3Device.h
index fe5f217..bbb6563 100644
--- a/services/camera/libcameraservice/device3/Camera3Device.h
+++ b/services/camera/libcameraservice/device3/Camera3Device.h
@@ -30,6 +30,7 @@
 #include "common/CameraDeviceBase.h"
 #include "device3/StatusTracker.h"
 #include "device3/Camera3BufferManager.h"
+#include "utils/TagMonitor.h"
 
 /**
  * Function pointer types with C calling convention to
@@ -855,6 +856,16 @@
 
     /**** End scope for mInFlightLock ****/
 
+    // Debug tracker for metadata tag value changes
+    // - Enabled with the -m <taglist> option to dumpsys, such as
+    //   dumpsys -m android.control.aeState,android.control.aeMode
+    // - Disabled with -m off
+    // - dumpsys -m 3a is a shortcut for ae/af/awbMode, State, and Triggers
+    TagMonitor mTagMonitor;
+
+    void monitorMetadata(TagMonitor::eventSource source, int64_t frameNumber,
+            nsecs_t timestamp, const CameraMetadata& metadata);
+
     /**
      * Static callback forwarding methods from HAL to instance
      */
diff --git a/services/camera/libcameraservice/utils/TagMonitor.cpp b/services/camera/libcameraservice/utils/TagMonitor.cpp
new file mode 100644
index 0000000..f1b65bd
--- /dev/null
+++ b/services/camera/libcameraservice/utils/TagMonitor.cpp
@@ -0,0 +1,281 @@
+/*
+ * Copyright (C) 2016 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_TAG "Camera3-TagMonitor"
+#define ATRACE_TAG ATRACE_TAG_CAMERA
+//#define LOG_NDEBUG 0
+
+#include "TagMonitor.h"
+
+#include <inttypes.h>
+#include <utils/Log.h>
+#include <camera/VendorTagDescriptor.h>
+
+namespace android {
+
+TagMonitor::TagMonitor():
+        mMonitoringEnabled(false),
+        mMonitoringEvents(kMaxMonitorEvents)
+{}
+
+const char* TagMonitor::k3aTags =
+        "android.control.aeMode, android.control.afMode, android.control.awbMode,"
+        "android.control.aeState, android.control.afState, android.control.awbState,"
+        "android.control.aePrecaptureTrigger, android.control.afTrigger,"
+        "android.control.aeRegions, android.control.awbRegions, android.control.afRegions,"
+        "android.control.aeExposureCompensation, android.control.aeLock, android.control.awbLock,"
+        "android.control.aeAntibandingMode, android.control.aeTargetFpsRange,"
+        "android.control.effectMode, android.control.mode, android.control.sceneMode,"
+        "android.control.videoStabilizationMode";
+
+void TagMonitor::parseTagsToMonitor(String8 tagNames) {
+    std::lock_guard<std::mutex> lock(mMonitorMutex);
+
+    // Expand shorthands
+    if (ssize_t idx = tagNames.find("3a") != -1) {
+        ssize_t end = tagNames.find(",", idx);
+        char* start = tagNames.lockBuffer(tagNames.size());
+        start[idx] = '\0';
+        char* rest = (end != -1) ? (start + end) : (start + tagNames.size());
+        tagNames = String8::format("%s%s%s", start, k3aTags, rest);
+    }
+
+    sp<VendorTagDescriptor> vTags =
+            VendorTagDescriptor::getGlobalVendorTagDescriptor();
+
+    bool gotTag = false;
+
+    char *tokenized = tagNames.lockBuffer(tagNames.size());
+    char *savePtr;
+    char *nextTagName = strtok_r(tokenized, ", ", &savePtr);
+    while (nextTagName != nullptr) {
+        uint32_t tag;
+        status_t res = CameraMetadata::getTagFromName(nextTagName, vTags.get(), &tag);
+        if (res != OK) {
+            ALOGW("%s: Unknown tag %s, ignoring", __FUNCTION__, nextTagName);
+        } else {
+            if (!gotTag) {
+                mMonitoredTagList.clear();
+                gotTag = true;
+            }
+            mMonitoredTagList.push_back(tag);
+        }
+        nextTagName = strtok_r(nullptr, ", ", &savePtr);
+    }
+
+    tagNames.unlockBuffer();
+
+    if (gotTag) {
+        // Got at least one new tag
+        mMonitoringEnabled = true;
+    }
+}
+
+void TagMonitor::disableMonitoring() {
+    mMonitoringEnabled = false;
+    mLastMonitoredRequestValues.clear();
+    mLastMonitoredResultValues.clear();
+}
+
+void TagMonitor::monitorMetadata(eventSource source, int64_t frameNumber, nsecs_t timestamp,
+        const CameraMetadata& metadata) {
+    if (!mMonitoringEnabled) return;
+
+    std::lock_guard<std::mutex> lock(mMonitorMutex);
+
+    if (timestamp == 0) {
+        timestamp = systemTime(SYSTEM_TIME_BOOTTIME);
+    }
+
+    for (auto tag : mMonitoredTagList) {
+        camera_metadata_ro_entry entry = metadata.find(tag);
+        CameraMetadata &lastValues = (source == REQUEST) ?
+                mLastMonitoredRequestValues : mLastMonitoredResultValues;
+        camera_metadata_entry lastEntry = lastValues.find(tag);
+
+        if (entry.count > 0) {
+            bool isDifferent = false;
+            if (lastEntry.count > 0) {
+                // Have a last value, compare to see if changed
+                if (lastEntry.type == entry.type &&
+                        lastEntry.count == entry.count) {
+                    // Same type and count, compare values
+                    size_t bytesPerValue = camera_metadata_type_size[lastEntry.type];
+                    size_t entryBytes = bytesPerValue * lastEntry.count;
+                    int cmp = memcmp(entry.data.u8, lastEntry.data.u8, entryBytes);
+                    if (cmp != 0) {
+                        isDifferent = true;
+                    }
+                } else {
+                    // Count or type has changed
+                    isDifferent = true;
+                }
+            } else {
+                // No last entry, so always consider to be different
+                isDifferent = true;
+            }
+
+            if (isDifferent) {
+                ALOGV("%s: Tag %s changed", __FUNCTION__, get_camera_metadata_tag_name(tag));
+                lastValues.update(entry);
+                mMonitoringEvents.emplace(source, frameNumber, timestamp, entry);
+            }
+        } else if (lastEntry.count > 0) {
+            // Value has been removed
+            ALOGV("%s: Tag %s removed", __FUNCTION__, get_camera_metadata_tag_name(tag));
+            lastValues.erase(tag);
+            entry.tag = tag;
+            entry.type = get_camera_metadata_tag_type(tag);
+            entry.count = 0;
+            mMonitoringEvents.emplace(source, frameNumber, timestamp, entry);
+        }
+    }
+}
+
+void TagMonitor::dumpMonitoredMetadata(int fd) {
+    std::lock_guard<std::mutex> lock(mMonitorMutex);
+
+    if (mMonitoringEnabled) {
+        dprintf(fd, "     Tag monitoring enabled for tags:\n");
+        for (uint32_t tag : mMonitoredTagList) {
+            dprintf(fd, "        %s.%s\n",
+                    get_camera_metadata_section_name(tag),
+                    get_camera_metadata_tag_name(tag));
+        }
+    } else {
+        dprintf(fd, "     Tag monitoring disabled (enable with -m <name1,..,nameN>)\n");
+    }
+    if (mMonitoringEvents.size() > 0) {
+        dprintf(fd, "     Monitored tag event log:\n");
+        for (const auto& event : mMonitoringEvents) {
+            int indentation = (event.source == REQUEST) ? 15 : 30;
+            dprintf(fd, "        f%d:%" PRId64 "ns: %*s%s.%s: ",
+                    event.frameNumber, event.timestamp,
+                    indentation,
+                    event.source == REQUEST ? "REQ:" : "RES:",
+                    get_camera_metadata_section_name(event.tag),
+                    get_camera_metadata_tag_name(event.tag));
+            if (event.newData.size() == 0) {
+                dprintf(fd, " (Removed)\n");
+            } else {
+                printData(fd, event.newData.data(), event.tag,
+                        event.type, event.newData.size() / camera_metadata_type_size[event.type],
+                        indentation + 18);
+            }
+        }
+    }
+
+}
+
+// TODO: Consolidate with printData from camera_metadata.h
+
+#define CAMERA_METADATA_ENUM_STRING_MAX_SIZE 29
+
+void TagMonitor::printData(int fd, const uint8_t *data_ptr, uint32_t tag,
+        int type, int count, int indentation) {
+    static int values_per_line[NUM_TYPES] = {
+        [TYPE_BYTE]     = 16,
+        [TYPE_INT32]    = 8,
+        [TYPE_FLOAT]    = 8,
+        [TYPE_INT64]    = 4,
+        [TYPE_DOUBLE]   = 4,
+        [TYPE_RATIONAL] = 4,
+    };
+    size_t type_size = camera_metadata_type_size[type];
+    char value_string_tmp[CAMERA_METADATA_ENUM_STRING_MAX_SIZE];
+    uint32_t value;
+
+    int lines = count / values_per_line[type];
+    if (count % values_per_line[type] != 0) lines++;
+
+    int index = 0;
+    int j, k;
+    for (j = 0; j < lines; j++) {
+        dprintf(fd, "%*s[", (j != 0) ? indentation + 4 : 0, "");
+        for (k = 0;
+             k < values_per_line[type] && count > 0;
+             k++, count--, index += type_size) {
+
+            switch (type) {
+                case TYPE_BYTE:
+                    value = *(data_ptr + index);
+                    if (camera_metadata_enum_snprint(tag,
+                                                     value,
+                                                     value_string_tmp,
+                                                     sizeof(value_string_tmp))
+                        == OK) {
+                        dprintf(fd, "%s ", value_string_tmp);
+                    } else {
+                        dprintf(fd, "%hhu ",
+                                *(data_ptr + index));
+                    }
+                    break;
+                case TYPE_INT32:
+                    value =
+                            *(int32_t*)(data_ptr + index);
+                    if (camera_metadata_enum_snprint(tag,
+                                                     value,
+                                                     value_string_tmp,
+                                                     sizeof(value_string_tmp))
+                        == OK) {
+                        dprintf(fd, "%s ", value_string_tmp);
+                    } else {
+                        dprintf(fd, "%" PRId32 " ",
+                                *(int32_t*)(data_ptr + index));
+                    }
+                    break;
+                case TYPE_FLOAT:
+                    dprintf(fd, "%0.8f ",
+                            *(float*)(data_ptr + index));
+                    break;
+                case TYPE_INT64:
+                    dprintf(fd, "%" PRId64 " ",
+                            *(int64_t*)(data_ptr + index));
+                    break;
+                case TYPE_DOUBLE:
+                    dprintf(fd, "%0.8f ",
+                            *(double*)(data_ptr + index));
+                    break;
+                case TYPE_RATIONAL: {
+                    int32_t numerator = *(int32_t*)(data_ptr + index);
+                    int32_t denominator = *(int32_t*)(data_ptr + index + 4);
+                    dprintf(fd, "(%d / %d) ",
+                            numerator, denominator);
+                    break;
+                }
+                default:
+                    dprintf(fd, "??? ");
+            }
+        }
+        dprintf(fd, "]\n");
+    }
+}
+
+template<typename T>
+TagMonitor::MonitorEvent::MonitorEvent(eventSource src, uint32_t frameNumber, nsecs_t timestamp,
+        const T &value) :
+        source(src),
+        frameNumber(frameNumber),
+        timestamp(timestamp),
+        tag(value.tag),
+        type(value.type),
+        newData(value.data.u8, value.data.u8 + camera_metadata_type_size[value.type] * value.count) {
+}
+
+TagMonitor::MonitorEvent::~MonitorEvent() {
+}
+
+} // namespace android
diff --git a/services/camera/libcameraservice/utils/TagMonitor.h b/services/camera/libcameraservice/utils/TagMonitor.h
new file mode 100644
index 0000000..d7aa419
--- /dev/null
+++ b/services/camera/libcameraservice/utils/TagMonitor.h
@@ -0,0 +1,107 @@
+/*
+ * Copyright (C) 2016 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 ANDROID_SERVERS_CAMERA_TAGMONITOR_H
+#define ANDROID_SERVERS_CAMERA_TAGMONITOR_H
+
+#include <vector>
+#include <atomic>
+#include <mutex>
+
+#include <utils/RefBase.h>
+#include <utils/String8.h>
+#include <utils/Timers.h>
+
+#include <media/RingBuffer.h>
+#include <system/camera_metadata.h>
+#include <camera/CameraMetadata.h>
+
+namespace android {
+
+/**
+ * A monitor for camera metadata values.
+ * Tracks changes to specified metadata values over time, keeping a circular
+ * buffer log that can be dumped at will. */
+class TagMonitor {
+  public:
+    enum eventSource {
+        REQUEST,
+        RESULT
+    };
+
+    TagMonitor();
+
+    // Parse tag name list (comma-separated) and if valid, enable monitoring
+    // If invalid, do nothing.
+    // Recognizes "3a" as a shortcut for enabling tracking 3A state, mode, and
+    // triggers
+    void parseTagsToMonitor(String8 tagNames);
+
+    // Disable monitoring; does not clear the event log
+    void disableMonitoring();
+
+    // Scan through the metadata and update the monitoring information
+    void monitorMetadata(eventSource source, int64_t frameNumber,
+            nsecs_t timestamp, const CameraMetadata& metadata);
+
+    // Dump current event log to the provided fd
+    void dumpMonitoredMetadata(int fd);
+
+  private:
+
+    static void printData(int fd, const uint8_t *data_ptr, uint32_t tag,
+            int type, int count, int indentation);
+
+    std::atomic<bool> mMonitoringEnabled;
+    std::mutex mMonitorMutex;
+
+    // Current tags to monitor and record changes to
+    std::vector<uint32_t> mMonitoredTagList;
+
+    // Latest-seen values of tracked tags
+    CameraMetadata mLastMonitoredRequestValues;
+    CameraMetadata mLastMonitoredResultValues;
+
+    /**
+     * A monitoring event
+     * Stores a new metadata field value and the timestamp at which it changed.
+     * Copies the source metadata value array and frees it on destruct.
+     */
+    struct MonitorEvent {
+        template<typename T>
+        MonitorEvent(eventSource src, uint32_t frameNumber, nsecs_t timestamp,
+                const T &newValue);
+        ~MonitorEvent();
+
+        eventSource source;
+        uint32_t frameNumber;
+        nsecs_t timestamp;
+        uint32_t tag;
+        uint8_t type;
+        std::vector<uint8_t> newData;
+    };
+
+    // A ring buffer for tracking the last kMaxMonitorEvents metadata changes
+    static const int kMaxMonitorEvents = 100;
+    RingBuffer<MonitorEvent> mMonitoringEvents;
+
+    // 3A fields to use with the "3a" option
+    static const char *k3aTags;
+};
+
+} // namespace android
+
+#endif