Merge "Make sure the encoder instance is safely release()'d before its" into klp-dev
diff --git a/camera/tests/ProCameraTests.cpp b/camera/tests/ProCameraTests.cpp
index f203949..e9aa99d 100644
--- a/camera/tests/ProCameraTests.cpp
+++ b/camera/tests/ProCameraTests.cpp
@@ -271,7 +271,6 @@
             CpuConsumer::LockedBuffer buf;
             status_t ret;
 
-            EXPECT_OK(ret);
             if (OK == (ret = consumer->lockNextBuffer(&buf))) {
 
                 dout << "Frame received on streamId = " << streamId <<
@@ -482,7 +481,7 @@
      * Creating a streaming request for these output streams from a template,
      *  and submit it
      */
-    void createSubmitRequestForStreams(uint8_t* streamIds, size_t count, int requestCount=-1) {
+    void createSubmitRequestForStreams(int32_t* streamIds, size_t count, int requestCount=-1) {
 
         ASSERT_NE((void*)NULL, streamIds);
         ASSERT_LT(0u, count);
@@ -629,7 +628,7 @@
 
             EXPECT_OK(mCamera->exclusiveTryLock());
 
-            uint8_t streams[] = { depthStreamId };
+            int32_t streams[] = { depthStreamId };
             ASSERT_NO_FATAL_FAILURE(createSubmitRequestForStreams(
                                                  streams,
                                                  /*count*/1));
@@ -706,7 +705,7 @@
     // set the output streams to just this stream ID
 
     // wow what a verbose API.
-    uint8_t allStreams[] = { streamId, depthStreamId };
+    int32_t allStreams[] = { streamId, depthStreamId };
     // IMPORTANT. bad things will happen if its not a uint8.
     size_t streamCount = sizeof(allStreams) / sizeof(allStreams[0]);
     camera_metadata_entry_t entry;
@@ -735,7 +734,7 @@
 
     free_camera_metadata(request);
 
-    for (int i = 0; i < streamCount; ++i) {
+    for (size_t i = 0; i < streamCount; ++i) {
         EXPECT_OK(mCamera->deleteStream(allStreams[i]));
     }
     EXPECT_OK(mCamera->exclusiveUnlock());
@@ -777,7 +776,7 @@
 
     // set the output streams to just this stream ID
 
-    uint8_t allStreams[] = { streamId };
+    int32_t allStreams[] = { streamId };
     camera_metadata_entry_t entry;
     uint32_t tag = static_cast<uint32_t>(ANDROID_REQUEST_OUTPUT_STREAMS);
     int find = find_camera_metadata_entry(request, tag, &entry);
@@ -848,7 +847,7 @@
     // set the output streams to just this stream ID
 
     // wow what a verbose API.
-    uint8_t allStreams[] = { streamId, depthStreamId };
+    int32_t allStreams[] = { streamId, depthStreamId };
     size_t streamCount = 2;
     camera_metadata_entry_t entry;
     uint32_t tag = static_cast<uint32_t>(ANDROID_REQUEST_OUTPUT_STREAMS);
@@ -923,7 +922,7 @@
 
     // set the output streams to just this stream ID
 
-    uint8_t allStreams[] = { streamId };
+    int32_t allStreams[] = { streamId };
     size_t streamCount = 1;
     camera_metadata_entry_t entry;
     uint32_t tag = static_cast<uint32_t>(ANDROID_REQUEST_OUTPUT_STREAMS);
@@ -974,7 +973,7 @@
 
     EXPECT_OK(mCamera->exclusiveTryLock());
 
-    uint8_t streams[] = { streamId };
+    int32_t streams[] = { streamId };
     ASSERT_NO_FATAL_FAILURE(createSubmitRequestForStreams(streams, /*count*/1));
 
     // Consume a couple of results
@@ -1002,7 +1001,7 @@
 
     EXPECT_OK(mCamera->exclusiveTryLock());
 
-    uint8_t streams[] = { streamId };
+    int32_t streams[] = { streamId };
     ASSERT_NO_FATAL_FAILURE(createSubmitRequestForStreams(streams, /*count*/1,
                                             /*requests*/TEST_CPU_FRAME_COUNT));
 
@@ -1049,7 +1048,7 @@
 
     EXPECT_OK(mCamera->exclusiveTryLock());
 
-    uint8_t streams[] = { streamId, depthStreamId };
+    int32_t streams[] = { streamId, depthStreamId };
     ASSERT_NO_FATAL_FAILURE(createSubmitRequestForStreams(streams, /*count*/2,
                                                     /*requests*/REQUEST_COUNT));
 
@@ -1128,7 +1127,7 @@
 
     EXPECT_OK(mCamera->exclusiveTryLock());
 
-    uint8_t streams[] = { streamId };
+    int32_t streams[] = { streamId };
     ASSERT_NO_FATAL_FAILURE(createSubmitRequestForStreams(streams, /*count*/1,
                                                      /*requests*/NUM_REQUESTS));
 
@@ -1172,7 +1171,6 @@
     }
 
     const int NUM_REQUESTS = 20 * TEST_CPU_FRAME_COUNT;
-    const int CONSECUTIVE_FAILS_ASSUME_TIME_OUT = 5;
 
     int streamId = -1;
     sp<CpuConsumer> consumer;
@@ -1183,7 +1181,7 @@
 
     EXPECT_OK(mCamera->exclusiveTryLock());
 
-    uint8_t streams[] = { streamId };
+    int32_t streams[] = { streamId };
     ASSERT_NO_FATAL_FAILURE(createSubmitRequestForStreams(streams, /*count*/1,
                                                      /*requests*/NUM_REQUESTS));
 
diff --git a/include/media/EffectsFactoryApi.h b/include/media/EffectsFactoryApi.h
index b1ed7b0..b1143b9 100644
--- a/include/media/EffectsFactoryApi.h
+++ b/include/media/EffectsFactoryApi.h
@@ -171,6 +171,30 @@
 ////////////////////////////////////////////////////////////////////////////////
 int EffectIsNullUuid(const effect_uuid_t *pEffectUuid);
 
+////////////////////////////////////////////////////////////////////////////////
+//
+//    Function:       EffectGetSubEffects
+//
+//    Description:    Returns the descriptors of the sub effects of the effect
+//                    whose uuid is pointed to by first argument.
+//
+//    Input:
+//          pEffectUuid:    pointer to the effect uuid.
+//          size:           size of the buffer pointed by pDescriptor.
+//
+//    Input/Output:
+//          pDescriptor:    address where to return the sub effect descriptors.
+//
+//    Output:
+//        returned value:    0          successful operation.
+//                          -ENODEV     factory failed to initialize
+//                          -EINVAL     invalid pEffectUuid or pDescriptor
+//                          -ENOENT     no effect with this uuid found
+//        *pDescriptor:     updated with the sub effect descriptors.
+//
+////////////////////////////////////////////////////////////////////////////////
+int EffectGetSubEffects(const effect_uuid_t *pEffectUuid, effect_descriptor_t *pDescriptors, size_t size);
+
 #if __cplusplus
 }  // extern "C"
 #endif
diff --git a/include/media/stagefright/MediaMuxer.h b/include/media/stagefright/MediaMuxer.h
index c1fdbad..ff6a66e 100644
--- a/include/media/stagefright/MediaMuxer.h
+++ b/include/media/stagefright/MediaMuxer.h
@@ -79,6 +79,16 @@
     status_t setOrientationHint(int degrees);
 
     /**
+     * Set the location.
+     * @param latitude The latitude in degree x 1000. Its value must be in the range
+     * [-900000, 900000].
+     * @param longitude The longitude in degree x 1000. Its value must be in the range
+     * [-1800000, 1800000].
+     * @return OK if no error.
+     */
+    status_t setLocation(int latitude, int longitude);
+
+    /**
      * Stop muxing.
      * This method is a blocking call. Depending on how
      * much data is bufferred internally, the time needed for stopping
diff --git a/media/libeffects/data/audio_effects.conf b/media/libeffects/data/audio_effects.conf
index 93f27cb..69a3c53 100644
--- a/media/libeffects/data/audio_effects.conf
+++ b/media/libeffects/data/audio_effects.conf
@@ -6,6 +6,23 @@
 #        }
 #    }
 libraries {
+# This is a proxy library that will be an abstraction for
+# the HW and SW effects
+
+  #proxy {
+    #path /system/lib/soundfx/libeffectproxy.so
+  #}
+
+# This is the SW implementation library of the effect
+  #libSW {
+    #path /system/lib/soundfx/libswwrapper.so
+  #}
+
+# This is the HW implementation library for the effect
+  #libHW {
+    #path /system/lib/soundfx/libhwwrapper.so
+  #}
+
   bundle {
     path /system/lib/soundfx/libbundlewrapper.so
   }
@@ -43,6 +60,28 @@
 #    }
 
 effects {
+
+# additions for the proxy implementation
+# Proxy implementation
+  #effectname {
+    #library proxy
+    #uuid  xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
+
+    # SW implemetation of the effect. Added as a node under the proxy to
+    # indicate this as a sub effect.
+      #libsw {
+         #library libSW
+         #uuid  yyyyyyyy-yyyy-yyyy-yyyy-yyyyyyyyyyyy
+      #} End of SW effect
+
+    # HW implementation of the effect. Added as a node under the proxy to
+    # indicate this as a sub effect.
+      #libhw {
+         #library libHW
+         #uuid  zzzzzzzz-zzzz-zzzz-zzzz-zzzzzzzzzzzz
+      #}End of HW effect
+  #} End of effect proxy
+
   bassboost {
     library bundle
     uuid 8631f300-72e2-11df-b57e-0002a5d5c51b
diff --git a/media/libeffects/factory/EffectsFactory.c b/media/libeffects/factory/EffectsFactory.c
index f158929..f8d6041 100644
--- a/media/libeffects/factory/EffectsFactory.c
+++ b/media/libeffects/factory/EffectsFactory.c
@@ -28,6 +28,9 @@
 
 static list_elem_t *gEffectList; // list of effect_entry_t: all currently created effects
 static list_elem_t *gLibraryList; // list of lib_entry_t: all currently loaded libraries
+// list of effect_descriptor and list of sub effects : all currently loaded
+// It does not contain effects without sub effects.
+static list_sub_elem_t *gSubEffectList;
 static pthread_mutex_t gLibLock = PTHREAD_MUTEX_INITIALIZER; // controls access to gLibraryList
 static uint32_t gNumEffects;         // total number number of effects
 static list_elem_t *gCurLib;    // current library in enumeration process
@@ -50,6 +53,8 @@
 static int loadLibrary(cnode *root, const char *name);
 static int loadEffects(cnode *root);
 static int loadEffect(cnode *node);
+// To get and add the effect pointed by the passed node to the gSubEffectList
+static int addSubEffect(cnode *root);
 static lib_entry_t *getLibrary(const char *path);
 static void resetEffectEnumeration();
 static uint32_t updateNumEffects();
@@ -57,6 +62,10 @@
                const effect_uuid_t *uuid,
                lib_entry_t **lib,
                effect_descriptor_t **desc);
+// To search a subeffect in the gSubEffectList
+int findSubEffect(const effect_uuid_t *uuid,
+               lib_entry_t **lib,
+               effect_descriptor_t **desc);
 static void dumpEffectDescriptor(effect_descriptor_t *desc, char *str, size_t len);
 static int stringToUuid(const char *str, effect_uuid_t *uuid);
 static int uuidToString(const effect_uuid_t *uuid, char *str, size_t maxLen);
@@ -287,7 +296,12 @@
 
     ret = findEffect(NULL, uuid, &l, &d);
     if (ret < 0){
-        goto exit;
+        // Sub effects are not associated with the library->effects,
+        // so, findEffect will fail. Search for the effect in gSubEffectList.
+        ret = findSubEffect(uuid, &l, &d);
+        if (ret < 0 ) {
+            goto exit;
+        }
     }
 
     // create effect in library
@@ -354,21 +368,27 @@
     }
     if (e1 == NULL) {
         ret = -ENOENT;
+        pthread_mutex_unlock(&gLibLock);
         goto exit;
     }
 
     // release effect in library
     if (fx->lib == NULL) {
         ALOGW("EffectRelease() fx %p library already unloaded", handle);
+        pthread_mutex_unlock(&gLibLock);
     } else {
         pthread_mutex_lock(&fx->lib->lock);
+        // Releasing the gLibLock here as the list access is over as the
+        // effect is removed from the list.
+        // If the gLibLock is not released, we will have a deadlock situation
+        // since we call the sub effect release inside the EffectRelease of Proxy
+        pthread_mutex_unlock(&gLibLock);
         fx->lib->desc->release_effect(fx->subItfe);
         pthread_mutex_unlock(&fx->lib->lock);
     }
     free(fx);
 
 exit:
-    pthread_mutex_unlock(&gLibLock);
     return ret;
 }
 
@@ -380,6 +400,49 @@
     return 1;
 }
 
+// Function to get the sub effect descriptors of the effect whose uuid
+// is pointed by the first argument. It searches the gSubEffectList for the
+// matching uuid and then copies the corresponding sub effect descriptors
+// to the inout param
+int EffectGetSubEffects(const effect_uuid_t *uuid,
+                        effect_descriptor_t *pDescriptors, size_t size)
+{
+   ALOGV("EffectGetSubEffects() UUID: %08X-%04X-%04X-%04X-%02X%02X%02X%02X%02X"
+          "%02X\n",uuid->timeLow, uuid->timeMid, uuid->timeHiAndVersion,
+          uuid->clockSeq, uuid->node[0], uuid->node[1],uuid->node[2],
+          uuid->node[3],uuid->node[4],uuid->node[5]);
+
+   // Check if the size of the desc buffer is large enough for 2 subeffects
+   if ((uuid == NULL) || (pDescriptors == NULL) ||
+       (size < 2*sizeof(effect_descriptor_t))) {
+       ALOGW("NULL pointer or insufficient memory. Cannot query subeffects");
+       return -EINVAL;
+   }
+   int ret = init();
+   if (ret < 0)
+      return ret;
+   list_sub_elem_t *e = gSubEffectList;
+   sub_effect_entry_t *subeffect;
+   effect_descriptor_t *d;
+   int count = 0;
+   while (e != NULL) {
+       d = (effect_descriptor_t*)e->object;
+       if (memcmp(uuid, &d->uuid, sizeof(effect_uuid_t)) == 0) {
+           ALOGV("EffectGetSubEffects: effect found in the list");
+           list_elem_t *subefx = e->sub_elem;
+           while (subefx != NULL) {
+               subeffect = (sub_effect_entry_t*)subefx->object;
+               d = (effect_descriptor_t*)(subeffect->object);
+               pDescriptors[count++] = *d;
+               subefx = subefx->next;
+           }
+           ALOGV("EffectGetSubEffects end - copied the sub effect descriptors");
+           return count;
+       }
+       e = e->next;
+   }
+   return -ENOENT;
+}
 /////////////////////////////////////////////////
 //      Local functions
 /////////////////////////////////////////////////
@@ -503,6 +566,65 @@
     return -EINVAL;
 }
 
+// This will find the library and UUID tags of the sub effect pointed by the
+// node, gets the effect descriptor and lib_entry_t and adds the subeffect -
+// sub_entry_t to the gSubEffectList
+int addSubEffect(cnode *root)
+{
+    ALOGV("addSubEffect");
+    cnode *node;
+    effect_uuid_t uuid;
+    effect_descriptor_t *d;
+    lib_entry_t *l;
+    list_elem_t *e;
+    node = config_find(root, LIBRARY_TAG);
+    if (node == NULL) {
+        return -EINVAL;
+    }
+    l = getLibrary(node->value);
+    if (l == NULL) {
+        ALOGW("addSubEffect() could not get library %s", node->value);
+        return -EINVAL;
+    }
+    node = config_find(root, UUID_TAG);
+    if (node == NULL) {
+        return -EINVAL;
+    }
+    if (stringToUuid(node->value, &uuid) != 0) {
+        ALOGW("addSubEffect() invalid uuid %s", node->value);
+        return -EINVAL;
+    }
+    d = malloc(sizeof(effect_descriptor_t));
+    if (l->desc->get_descriptor(&uuid, d) != 0) {
+        char s[40];
+        uuidToString(&uuid, s, 40);
+        ALOGW("Error querying effect %s on lib %s", s, l->name);
+        free(d);
+        return -EINVAL;
+    }
+#if (LOG_NDEBUG==0)
+    char s[256];
+    dumpEffectDescriptor(d, s, 256);
+    ALOGV("addSubEffect() read descriptor %p:%s",d, s);
+#endif
+    if (EFFECT_API_VERSION_MAJOR(d->apiVersion) !=
+            EFFECT_API_VERSION_MAJOR(EFFECT_CONTROL_API_VERSION)) {
+        ALOGW("Bad API version %08x on lib %s", d->apiVersion, l->name);
+        free(d);
+        return -EINVAL;
+    }
+    sub_effect_entry_t *sub_effect = malloc(sizeof(sub_effect_entry_t));
+    sub_effect->object = d;
+    // lib_entry_t is stored since the sub effects are not linked to the library
+    sub_effect->lib = l;
+    e = malloc(sizeof(list_elem_t));
+    e->object = sub_effect;
+    e->next = gSubEffectList->sub_elem;
+    gSubEffectList->sub_elem = e;
+    ALOGV("addSubEffect end");
+    return 0;
+}
+
 int loadEffects(cnode *root)
 {
     cnode *node;
@@ -571,9 +693,101 @@
     e->next = l->effects;
     l->effects = e;
 
+    // After the UUID node in the config_tree, if node->next is valid,
+    // that would be sub effect node.
+    // Find the sub effects and add them to the gSubEffectList
+    node = node->next;
+    int count = 2;
+    bool hwSubefx = false, swSubefx = false;
+    list_sub_elem_t *sube = NULL;
+    if (node != NULL) {
+        ALOGV("Adding the effect to gEffectSubList as there are sub effects");
+        sube = malloc(sizeof(list_sub_elem_t));
+        sube->object = d;
+        sube->sub_elem = NULL;
+        sube->next = gSubEffectList;
+        gSubEffectList = sube;
+    }
+    while (node != NULL && count) {
+       if (addSubEffect(node)) {
+           ALOGW("loadEffect() could not add subEffect %s", node->value);
+           // Change the gSubEffectList to point to older list;
+           gSubEffectList = sube->next;
+           free(sube->sub_elem);// Free an already added sub effect
+           sube->sub_elem = NULL;
+           free(sube);
+           return -ENOENT;
+       }
+       sub_effect_entry_t *subEntry = (sub_effect_entry_t*)gSubEffectList->sub_elem->object;
+       effect_descriptor_t *subEffectDesc = (effect_descriptor_t*)(subEntry->object);
+       // Since we return a dummy descriptor for the proxy during
+       // get_descriptor call,we replace it with the correspoding
+       // sw effect descriptor, but with Proxy UUID
+       // check for Sw desc
+        if (!((subEffectDesc->flags & EFFECT_FLAG_HW_ACC_MASK) ==
+                                           EFFECT_FLAG_HW_ACC_TUNNEL)) {
+             swSubefx = true;
+             *d = *subEffectDesc;
+             d->uuid = uuid;
+             ALOGV("loadEffect() Changed the Proxy desc");
+       } else
+           hwSubefx = true;
+       count--;
+       node = node->next;
+    }
+    // 1 HW and 1 SW sub effect found. Set the offload flag in the Proxy desc
+    if (hwSubefx && swSubefx) {
+        d->flags |= EFFECT_FLAG_OFFLOAD_SUPPORTED;
+    }
     return 0;
 }
 
+// Searches the sub effect matching to the specified uuid
+// in the gSubEffectList. It gets the lib_entry_t for
+// the matched sub_effect . Used in EffectCreate of sub effects
+int findSubEffect(const effect_uuid_t *uuid,
+               lib_entry_t **lib,
+               effect_descriptor_t **desc)
+{
+    list_sub_elem_t *e = gSubEffectList;
+    list_elem_t *subefx;
+    sub_effect_entry_t *effect;
+    lib_entry_t *l = NULL;
+    effect_descriptor_t *d = NULL;
+    int found = 0;
+    int ret = 0;
+
+    if (uuid == NULL)
+        return -EINVAL;
+
+    while (e != NULL && !found) {
+        subefx = (list_elem_t*)(e->sub_elem);
+        while (subefx != NULL) {
+            effect = (sub_effect_entry_t*)subefx->object;
+            l = (lib_entry_t *)effect->lib;
+            d = (effect_descriptor_t *)effect->object;
+            if (memcmp(&d->uuid, uuid, sizeof(effect_uuid_t)) == 0) {
+                ALOGV("uuid matched");
+                found = 1;
+                break;
+            }
+            subefx = subefx->next;
+        }
+        e = e->next;
+    }
+    if (!found) {
+        ALOGV("findSubEffect() effect not found");
+        ret = -ENOENT;
+    } else {
+        ALOGV("findSubEffect() found effect: %s in lib %s", d->name, l->name);
+        *lib = l;
+        if (desc != NULL) {
+            *desc = d;
+        }
+    }
+    return ret;
+}
+
 lib_entry_t *getLibrary(const char *name)
 {
     list_elem_t *e;
diff --git a/media/libeffects/factory/EffectsFactory.h b/media/libeffects/factory/EffectsFactory.h
index c1d4319..147ff18 100644
--- a/media/libeffects/factory/EffectsFactory.h
+++ b/media/libeffects/factory/EffectsFactory.h
@@ -32,6 +32,15 @@
     struct list_elem_s *next;
 } list_elem_t;
 
+// Structure used for storing effects with their sub effects.
+// Used in creating gSubEffectList. Here,
+// object holds the effect desc and the list sub_elem holds the sub effects
+typedef struct list_sub_elem_s {
+    void *object;
+    list_elem_t *sub_elem;
+    struct list_sub_elem_s *next;
+} list_sub_elem_t;
+
 typedef struct lib_entry_s {
     audio_effect_library_t *desc;
     char *name;
@@ -47,6 +56,16 @@
     lib_entry_t *lib;
 } effect_entry_t;
 
+// Structure used to store the lib entry
+// and the descriptor of the sub effects.
+// The library entry is to be stored in case of
+// sub effects as the sub effects are not linked
+// to the library list - gLibraryList.
+typedef struct sub_effect_entry_s {
+    lib_entry_t *lib;
+    void *object;
+} sub_effect_entry_t;
+
 #if __cplusplus
 }  // extern "C"
 #endif
diff --git a/media/libeffects/proxy/Android.mk b/media/libeffects/proxy/Android.mk
new file mode 100644
index 0000000..01b3be1
--- /dev/null
+++ b/media/libeffects/proxy/Android.mk
@@ -0,0 +1,34 @@
+# Copyright 2013 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.
+
+LOCAL_PATH:= $(call my-dir)
+include $(CLEAR_VARS)
+LOCAL_MODULE:= libeffectproxy
+LOCAL_MODULE_PATH := $(TARGET_OUT_SHARED_LIBRARIES)/soundfx
+LOCAL_MODULE_TAGS := optional
+
+
+LOCAL_SRC_FILES := \
+        EffectProxy.cpp
+
+LOCAL_CFLAGS+= -fvisibility=hidden
+
+LOCAL_SHARED_LIBRARIES := liblog libcutils libutils libdl libeffects
+
+LOCAL_C_INCLUDES := \
+        system/media/audio_effects/include \
+        bionic/libc/include
+
+include $(BUILD_SHARED_LIBRARY)
+
diff --git a/media/libeffects/proxy/EffectProxy.cpp b/media/libeffects/proxy/EffectProxy.cpp
new file mode 100644
index 0000000..77c6e89
--- /dev/null
+++ b/media/libeffects/proxy/EffectProxy.cpp
@@ -0,0 +1,298 @@
+/*
+ * Copyright (C) 2013 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 "EffectProxy"
+//#define LOG_NDEBUG 0
+
+#include <cutils/log.h>
+#include <assert.h>
+#include <stdlib.h>
+#include <string.h>
+#include <new>
+#include <EffectProxy.h>
+#include <utils/threads.h>
+#include <media/EffectsFactoryApi.h>
+
+namespace android {
+// This is a dummy proxy descriptor just to return to Factory during the initial
+// GetDescriptor call. Later in the factory, it is replaced with the
+// SW sub effect descriptor
+const effect_descriptor_t gProxyDescriptor = {
+        EFFECT_UUID_INITIALIZER, // type
+        EFFECT_UUID_INITIALIZER, // uuid
+        EFFECT_CONTROL_API_VERSION, //version of effect control API
+        (EFFECT_FLAG_TYPE_INSERT | EFFECT_FLAG_INSERT_LAST |
+         EFFECT_FLAG_VOLUME_CTRL), // effect capability flags
+        0, // CPU load
+        1, // Data memory
+        "Proxy", //effect name
+        "AOSP", //implementor name
+};
+
+
+static const effect_descriptor_t *const gDescriptors[] =
+{
+    &gProxyDescriptor,
+};
+
+int EffectProxyCreate(const effect_uuid_t *uuid,
+                            int32_t             sessionId,
+                            int32_t             ioId,
+                           effect_handle_t  *pHandle) {
+
+    effect_descriptor_t* desc;
+    EffectContext* pContext;
+    if (pHandle == NULL || uuid == NULL) {
+        ALOGE("EffectProxyCreate() called with NULL pointer");
+        return -EINVAL;
+    }
+    ALOGV("EffectProxyCreate start..");
+    pContext = new EffectContext;
+    pContext->sessionId = sessionId;
+    pContext->ioId = ioId;
+    pContext->uuid = *uuid;
+    pContext->common_itfe = &gEffectInterface;
+    // The sub effects will be created in effect_command when the first command
+    // for the effect is received
+    pContext->eHandle[SUB_FX_HOST] = pContext->eHandle[SUB_FX_OFFLOAD] = NULL;
+
+    // Get the HW and SW sub effect descriptors from the effects factory
+    desc = new effect_descriptor_t[SUB_FX_COUNT];
+    pContext->desc = new effect_descriptor_t[SUB_FX_COUNT];
+    int retValue = EffectGetSubEffects(uuid, desc,
+                                sizeof(effect_descriptor_t) * SUB_FX_COUNT);
+    // EffectGetSubEffects returns the number of sub-effects copied.
+    if (retValue != SUB_FX_COUNT) {
+       ALOGE("EffectCreate() could not get the sub effects");
+       delete desc;
+       delete pContext->desc;
+       return -EINVAL;
+    }
+    // Check which is the HW descriptor and copy the descriptors
+    // to the Context desc array
+    // Also check if there is only one HW and one SW descriptor.
+    // HW descriptor alone has the HW_TUNNEL flag.
+    if ((desc[0].flags & EFFECT_FLAG_HW_ACC_TUNNEL) &&
+       !(desc[1].flags & EFFECT_FLAG_HW_ACC_TUNNEL)) {
+        pContext->desc[SUB_FX_OFFLOAD] = desc[0];
+        pContext->desc[SUB_FX_HOST] = desc[1];
+    }
+    else if ((desc[1].flags & EFFECT_FLAG_HW_ACC_TUNNEL) &&
+             !(desc[0].flags & EFFECT_FLAG_HW_ACC_TUNNEL)) {
+        pContext->desc[SUB_FX_HOST] = desc[0];
+        pContext->desc[SUB_FX_OFFLOAD] = desc[1];
+    }
+    delete desc;
+#if (LOG_NDEBUG == 0)
+    effect_uuid_t uuid_print = pContext->desc[SUB_FX_HOST].uuid;
+    ALOGV("EffectCreate() UUID of HOST: %08X-%04X-%04X-%04X-%02X%02X%02X%02X"
+        "%02X%02X\n",uuid_print.timeLow, uuid_print.timeMid,
+        uuid_print.timeHiAndVersion, uuid_print.clockSeq, uuid_print.node[0],
+        uuid_print.node[1], uuid_print.node[2], uuid_print.node[3],
+        uuid_print.node[4], uuid_print.node[5]);
+    ALOGV("EffectCreate() UUID of OFFLOAD: %08X-%04X-%04X-%04X-%02X%02X%02X%02X"
+        "%02X%02X\n", uuid_print.timeLow, uuid_print.timeMid,
+        uuid_print.timeHiAndVersion, uuid_print.clockSeq, uuid_print.node[0],
+        uuid_print.node[1], uuid_print.node[2], uuid_print.node[3],
+        uuid_print.node[4], uuid_print.node[5]);
+#endif
+    *pHandle = (effect_handle_t)pContext;
+    ALOGV("EffectCreate end");
+    return 0;
+} //end EffectProxyCreate
+
+int EffectProxyRelease(effect_handle_t handle) {
+    EffectContext * pContext = (EffectContext *)handle;
+    if (pContext == NULL) {
+        ALOGV("ERROR : EffectRelease called with NULL pointer");
+        return -EINVAL;
+    }
+    ALOGV("EffectRelease");
+    delete pContext->desc;
+    if (pContext->eHandle[SUB_FX_HOST])
+       EffectRelease(pContext->eHandle[SUB_FX_HOST]);
+    if (pContext->eHandle[SUB_FX_OFFLOAD])
+       EffectRelease(pContext->eHandle[SUB_FX_OFFLOAD]);
+    delete pContext;
+    pContext = NULL;
+    return 0;
+} /*end EffectProxyRelease */
+
+int EffectProxyGetDescriptor(const effect_uuid_t *uuid,
+                                   effect_descriptor_t *pDescriptor) {
+    const effect_descriptor_t *desc = NULL;
+
+    if (pDescriptor == NULL || uuid == NULL) {
+        ALOGV("EffectGetDescriptor() called with NULL pointer");
+        return -EINVAL;
+    }
+    desc = &gProxyDescriptor;
+    *pDescriptor = *desc;
+    return 0;
+} /* end EffectProxyGetDescriptor */
+
+/* Effect Control Interface Implementation: Process */
+int Effect_process(effect_handle_t     self,
+                              audio_buffer_t         *inBuffer,
+                              audio_buffer_t         *outBuffer) {
+
+    EffectContext *pContext = (EffectContext *) self;
+    int ret = 0;
+    if (pContext != NULL) {
+        int index = pContext->index;
+        // if the index refers to HW , do not do anything. Just return.
+        if (index == SUB_FX_HOST) {
+            ALOGV("Calling CoreProcess");
+            ret = (*pContext->eHandle[index])->process(pContext->eHandle[index],
+                                                       inBuffer, outBuffer);
+        }
+    }
+    return ret;
+}   /* end Effect_process */
+
+/* Effect Control Interface Implementation: Command */
+int Effect_command(effect_handle_t  self,
+                              uint32_t            cmdCode,
+                              uint32_t            cmdSize,
+                              void                *pCmdData,
+                              uint32_t            *replySize,
+                              void                *pReplyData) {
+
+    EffectContext *pContext = (EffectContext *) self;
+    int status;
+    if (pContext == NULL) {
+        ALOGV("Effect_command() Proxy context is NULL");
+        return -EINVAL;
+    }
+    if (pContext->eHandle[SUB_FX_HOST] == NULL) {
+        ALOGV("Effect_command() Calling HOST EffectCreate");
+        status = EffectCreate(&pContext->desc[SUB_FX_HOST].uuid,
+                              pContext->sessionId, pContext->ioId,
+                              &(pContext->eHandle[SUB_FX_HOST]));
+        if (status != NO_ERROR || (pContext->eHandle[SUB_FX_HOST] == NULL)) {
+            ALOGV("Effect_command() Error creating SW sub effect");
+            return status;
+        }
+    }
+    if (pContext->eHandle[SUB_FX_OFFLOAD] == NULL) {
+        ALOGV("Effect_command() Calling OFFLOAD EffectCreate");
+        status = EffectCreate(&pContext->desc[SUB_FX_OFFLOAD].uuid,
+                              pContext->sessionId, pContext->ioId,
+                              &(pContext->eHandle[SUB_FX_OFFLOAD]));
+        if (status != NO_ERROR || (pContext->eHandle[SUB_FX_OFFLOAD] == NULL)) {
+            ALOGV("Effect_command() Error creating HW effect");
+            // Do not return error here as SW effect is created
+            // Return error if the CMD_OFFLOAD sends the index as OFFLOAD
+        }
+        pContext->index = SUB_FX_HOST;
+    }
+    // EFFECT_CMD_OFFLOAD used to (1) send whether the thread is offload or not
+    // (2) Send the ioHandle of the effectThread when the effect
+    // is moved from one type of thread to another.
+    // pCmdData points to a memory holding effect_offload_param_t structure
+    if (cmdCode == EFFECT_CMD_OFFLOAD) {
+        ALOGV("Effect_command() cmdCode = EFFECT_CMD_OFFLOAD");
+        if (cmdSize == 0 || pCmdData == NULL) {
+            ALOGV("effectsOffload: Effect_command: CMD_OFFLOAD has no data");
+             *(int*)pReplyData = FAILED_TRANSACTION;
+            return FAILED_TRANSACTION;
+        }
+        effect_offload_param_t* offloadParam = (effect_offload_param_t*)pCmdData;
+        // Assign the effect context index based on isOffload field of the structure
+        pContext->index = offloadParam->isOffload ? SUB_FX_OFFLOAD : SUB_FX_HOST;
+        // if the index is HW and the HW effect is unavailable, return error
+        // and reset the index to SW
+        if (pContext->eHandle[pContext->index] == NULL) {
+            ALOGV("Effect_command()CMD_OFFLOAD sub effect unavailable");
+            *(int*)pReplyData = FAILED_TRANSACTION;
+            return FAILED_TRANSACTION;
+        }
+        pContext->ioId = offloadParam->ioHandle;
+        ALOGV("Effect_command()CMD_OFFLOAD index:%d io %d", pContext->index, pContext->ioId);
+        // Update the DSP wrapper with the new ioHandle.
+        // Pass the OFFLOAD command to the wrapper.
+        // The DSP wrapper needs to handle this CMD
+        if (pContext->eHandle[SUB_FX_OFFLOAD])
+            status = (*pContext->eHandle[SUB_FX_OFFLOAD])->command(
+                             pContext->eHandle[SUB_FX_OFFLOAD], cmdCode, cmdSize,
+                             pCmdData, replySize, pReplyData);
+        return status;
+    }
+
+    int index = pContext->index;
+    if (index != SUB_FX_HOST && index != SUB_FX_OFFLOAD) {
+        ALOGV("Effect_command: effect index is neither offload nor host");
+        return -EINVAL;
+    }
+    ALOGV("Effect_command: pContext->eHandle[%d]: %p",
+            index, pContext->eHandle[index]);
+    if (pContext->eHandle[SUB_FX_HOST])
+         (*pContext->eHandle[SUB_FX_HOST])->command(
+                             pContext->eHandle[SUB_FX_HOST], cmdCode, cmdSize,
+                             pCmdData, replySize, pReplyData);
+    if (pContext->eHandle[SUB_FX_OFFLOAD]) {
+        // In case of SET CMD, when the offload stream is unavailable,
+        // we will store the effect param values in the DSP effect wrapper.
+        // When the offload effects get enabled, we send these values to the
+        // DSP during Effect_config.
+        // So,we send the params to DSP wrapper also
+        (*pContext->eHandle[SUB_FX_OFFLOAD])->command(
+                         pContext->eHandle[SUB_FX_OFFLOAD], cmdCode, cmdSize,
+                         pCmdData, replySize, pReplyData);
+    }
+    return 0;
+}    /* end Effect_command */
+
+
+/* Effect Control Interface Implementation: get_descriptor */
+int Effect_getDescriptor(effect_handle_t   self,
+                         effect_descriptor_t *pDescriptor) {
+
+    EffectContext * pContext = (EffectContext *) self;
+    const effect_descriptor_t *desc;
+
+    ALOGV("Effect_getDescriptor");
+    if (pContext == NULL || pDescriptor == NULL) {
+        ALOGV("Effect_getDescriptor() invalid param");
+        return -EINVAL;
+    }
+    if (pContext->desc == NULL) {
+        ALOGV("Effect_getDescriptor() could not get descriptor");
+        return -EINVAL;
+    }
+    desc = &pContext->desc[SUB_FX_HOST];
+    *pDescriptor = *desc;
+    pDescriptor->uuid = pContext->uuid; // Replace the uuid with the Proxy UUID
+    // Also set/clear the EFFECT_FLAG_OFFLOAD_SUPPORTED flag based on the sub effects availability
+    if (pContext->eHandle[SUB_FX_OFFLOAD] != NULL)
+        pDescriptor->flags |= EFFECT_FLAG_OFFLOAD_SUPPORTED;
+    else
+        pDescriptor->flags &= ~EFFECT_FLAG_OFFLOAD_SUPPORTED;
+    return 0;
+} /* end Effect_getDescriptor */
+
+} // namespace android
+
+__attribute__ ((visibility ("default")))
+audio_effect_library_t AUDIO_EFFECT_LIBRARY_INFO_SYM = {
+    tag : AUDIO_EFFECT_LIBRARY_TAG,
+    version : EFFECT_LIBRARY_API_VERSION,
+    name : "Effect Proxy",
+    implementor : "AOSP",
+    create_effect : android::EffectProxyCreate,
+    release_effect : android::EffectProxyRelease,
+    get_descriptor : android::EffectProxyGetDescriptor,
+};
diff --git a/media/libeffects/proxy/EffectProxy.h b/media/libeffects/proxy/EffectProxy.h
new file mode 100644
index 0000000..8992f93
--- /dev/null
+++ b/media/libeffects/proxy/EffectProxy.h
@@ -0,0 +1,75 @@
+/*
+ * Copyright (C) 2013 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.
+ */
+
+#include <hardware/audio.h>
+#include <hardware/audio_effect.h>
+namespace android {
+enum {
+    SUB_FX_HOST,       // Index of HOST in the descriptor and handle arrays
+                       // of the Proxy context
+    SUB_FX_OFFLOAD,    // Index of OFFLOAD in the descriptor and handle arrays
+                       // of the Proxy context
+    SUB_FX_COUNT       // The number of sub effects for a Proxy(1 HW, 1 SW)
+};
+#if __cplusplus
+extern "C" {
+#endif
+
+int EffectProxyCreate(const effect_uuid_t *uuid,
+                          int32_t             sessionId,
+                          int32_t             ioId,
+                          effect_handle_t  *pHandle);
+int EffectProxyRelease(effect_handle_t handle);
+int EffectProxyGetDescriptor(const effect_uuid_t *uuid,
+                                 effect_descriptor_t *pDescriptor);
+/* Effect Control Interface Implementation: Process */
+int Effect_process(effect_handle_t     self,
+                            audio_buffer_t         *inBuffer,
+                            audio_buffer_t         *outBuffer);
+
+/* Effect Control Interface Implementation: Command */
+int Effect_command(effect_handle_t  self,
+                            uint32_t            cmdCode,
+                            uint32_t            cmdSize,
+                            void                *pCmdData,
+                            uint32_t            *replySize,
+                            void                *pReplyData);
+int Effect_getDescriptor(effect_handle_t   self,
+                       effect_descriptor_t *pDescriptor);
+
+const struct effect_interface_s gEffectInterface = {
+  Effect_process,
+  Effect_command,
+  Effect_getDescriptor,
+  NULL,
+};
+
+struct EffectContext {
+  const struct effect_interface_s  *common_itfe; // Holds the itfe of the Proxy
+  effect_descriptor_t*  desc;                    // Points to the sub effect descriptors
+  effect_handle_t       eHandle[SUB_FX_COUNT];   // The effect handles of the sub effects
+  int                   index;       // The index that is currently active - HOST or OFFLOAD
+  int32_t               sessionId;   // The sessiond in which the effect is created.
+                                     // Stored in context to pass on to sub effect creation
+  int32_t               ioId;        // The ioId in which the effect is created.
+                                     // Stored in context to pass on to sub effect creation
+  effect_uuid_t         uuid;        // UUID of the Proxy
+};
+
+#if __cplusplus
+}  // extern "C"
+#endif
+} //namespace android
diff --git a/media/libstagefright/MediaCodec.cpp b/media/libstagefright/MediaCodec.cpp
index 66a0b4e..e0686be 100644
--- a/media/libstagefright/MediaCodec.cpp
+++ b/media/libstagefright/MediaCodec.cpp
@@ -31,6 +31,7 @@
 #include <media/stagefright/foundation/hexdump.h>
 #include <media/stagefright/ACodec.h>
 #include <media/stagefright/BufferProducerWrapper.h>
+#include <media/stagefright/MediaCodecList.h>
 #include <media/stagefright/MediaDefs.h>
 #include <media/stagefright/MediaErrors.h>
 #include <media/stagefright/MetaData.h>
@@ -104,10 +105,24 @@
     bool needDedicatedLooper = false;
     if (nameIsType && !strncasecmp(name, "video/", 6)) {
         needDedicatedLooper = true;
-    } else if (!nameIsType && !strncmp(name, "OMX.TI.DUCATI1.VIDEO.", 21)) {
-        needDedicatedLooper = true;
-    } else if (!nameIsType && !strncmp(name, "OMX.qcom.video.decoder.avc.secure", 33)) {
-        needDedicatedLooper = true;
+    } else {
+        AString tmp = name;
+        if (tmp.endsWith(".secure")) {
+            tmp.erase(tmp.size() - 7, 7);
+        }
+        const MediaCodecList *mcl = MediaCodecList::getInstance();
+        ssize_t codecIdx = mcl->findCodecByName(tmp.c_str());
+        if (codecIdx >= 0) {
+            Vector<AString> types;
+            if (mcl->getSupportedTypes(codecIdx, &types) == OK) {
+                for (int i = 0; i < types.size(); i++) {
+                    if (types[i].startsWith("video/")) {
+                        needDedicatedLooper = true;
+                        break;
+                    }
+                }
+            }
+        }
     }
 
     if (needDedicatedLooper) {
diff --git a/media/libstagefright/MediaMuxer.cpp b/media/libstagefright/MediaMuxer.cpp
index 94ce5de..d87e910 100644
--- a/media/libstagefright/MediaMuxer.cpp
+++ b/media/libstagefright/MediaMuxer.cpp
@@ -103,6 +103,16 @@
     return OK;
 }
 
+status_t MediaMuxer::setLocation(int latitude, int longitude) {
+    Mutex::Autolock autoLock(mMuxerLock);
+    if (mState != INITIALIZED) {
+        ALOGE("setLocation() must be called before start().");
+        return INVALID_OPERATION;
+    }
+    ALOGV("Setting location: latitude = %d, longitude = %d", latitude, longitude);
+    return mWriter->setGeoData(latitude, longitude);
+}
+
 status_t MediaMuxer::start() {
     Mutex::Autolock autoLock(mMuxerLock);
     if (mState == INITIALIZED) {
diff --git a/services/audioflinger/AudioFlinger.cpp b/services/audioflinger/AudioFlinger.cpp
index b8a6b37..626b5c2 100644
--- a/services/audioflinger/AudioFlinger.cpp
+++ b/services/audioflinger/AudioFlinger.cpp
@@ -98,6 +98,11 @@
 size_t AudioFlinger::mTeeSinkTrackFrames = kTeeSinkTrackFramesDefault;
 #endif
 
+//TODO: remove when effect offload is implemented
+// In order to avoid invalidating offloaded tracks each time a Visualizer is turned on and off
+// we define a minimum time during which a global effect is considered enabled.
+static const nsecs_t kMinGlobalEffectEnabletimeNs = seconds(7200);
+
 // ----------------------------------------------------------------------------
 
 static int load_audio_interface(const char *if_name, audio_hw_device_t **dev)
@@ -141,7 +146,8 @@
       mMode(AUDIO_MODE_INVALID),
       mBtNrecIsOff(false),
       mIsLowRamDevice(true),
-      mIsDeviceTypeKnown(false)
+      mIsDeviceTypeKnown(false),
+      mGlobalEffectEnableTime(0)
 {
     getpid_cached = getpid();
     char value[PROPERTY_VALUE_MAX];
@@ -2314,6 +2320,38 @@
     return NO_ERROR;
 }
 
+bool AudioFlinger::isGlobalEffectEnabled_l()
+{
+    if (mGlobalEffectEnableTime != 0 &&
+            ((systemTime() - mGlobalEffectEnableTime) < kMinGlobalEffectEnabletimeNs)) {
+        return true;
+    }
+
+    for (size_t i = 0; i < mPlaybackThreads.size(); i++) {
+        sp<EffectChain> ec =
+                mPlaybackThreads.valueAt(i)->getEffectChain_l(AUDIO_SESSION_OUTPUT_MIX);
+        if (ec != 0 && ec->isEnabled()) {
+            return true;
+        }
+    }
+    return false;
+}
+
+void AudioFlinger::onGlobalEffectEnable()
+{
+    Mutex::Autolock _l(mLock);
+
+    mGlobalEffectEnableTime = systemTime();
+
+    for (size_t i = 0; i < mPlaybackThreads.size(); i++) {
+        sp<PlaybackThread> t = mPlaybackThreads.valueAt(i);
+        if (t->mType == ThreadBase::OFFLOAD) {
+            t->invalidateTracks(AUDIO_STREAM_MUSIC);
+        }
+    }
+
+}
+
 struct Entry {
 #define MAX_NAME 32     // %Y%m%d%H%M%S_%d.wav
     char mName[MAX_NAME];
diff --git a/services/audioflinger/AudioFlinger.h b/services/audioflinger/AudioFlinger.h
index 5df04f4..0992308 100644
--- a/services/audioflinger/AudioFlinger.h
+++ b/services/audioflinger/AudioFlinger.h
@@ -466,6 +466,10 @@
                 void        removeClient_l(pid_t pid);
                 void        removeNotificationClient(pid_t pid);
 
+                //TODO: remove when effect offload is implemented
+                bool isGlobalEffectEnabled_l();
+                void onGlobalEffectEnable();
+
     class AudioHwDevice {
     public:
         enum Flags {
@@ -641,6 +645,8 @@
 private:
     bool    mIsLowRamDevice;
     bool    mIsDeviceTypeKnown;
+    //TODO: remove when effect offload is implemented
+    nsecs_t mGlobalEffectEnableTime;  // when a global effect was last enabled
 };
 
 #undef INCLUDING_FROM_AUDIOFLINGER_H
diff --git a/services/audioflinger/Effects.cpp b/services/audioflinger/Effects.cpp
index d5a21a7..86671a9 100644
--- a/services/audioflinger/Effects.cpp
+++ b/services/audioflinger/Effects.cpp
@@ -593,17 +593,6 @@
                 h->setEnabled(enabled);
             }
         }
-//EL_FIXME not sure why this is needed?
-//        sp<ThreadBase> thread = mThread.promote();
-//        if (thread == 0) {
-//            return NO_ERROR;
-//        }
-//
-//        if ((thread->type() == ThreadBase::OFFLOAD) && (enabled)) {
-//            PlaybackThread *p = (PlaybackThread *)thread.get();
-//            ALOGV("setEnabled: Offload, invalidate tracks");
-//            p->invalidateTracks(AUDIO_STREAM_MUSIC);
-//        }
     }
     return NO_ERROR;
 }
@@ -942,6 +931,17 @@
             thread->checkSuspendOnEffectEnabled(mEffect, false, mEffect->sessionId());
         }
         mEnabled = false;
+    } else {
+        //TODO: remove when effect offload is implemented
+        if (thread != 0) {
+            if ((thread->type() == ThreadBase::OFFLOAD)) {
+                PlaybackThread *t = (PlaybackThread *)thread.get();
+                t->invalidateTracks(AUDIO_STREAM_MUSIC);
+            }
+            if (mEffect->sessionId() == AUDIO_SESSION_OUTPUT_MIX) {
+                thread->mAudioFlinger->onGlobalEffectEnable();
+            }
+        }
     }
     return status;
 }
@@ -1728,4 +1728,16 @@
     }
 }
 
+bool AudioFlinger::EffectChain::isEnabled()
+{
+    Mutex::Autolock _l(mLock);
+    size_t size = mEffects.size();
+    for (size_t i = 0; i < size; i++) {
+        if (mEffects[i]->isEnabled()) {
+            return true;
+        }
+    }
+    return false;
+}
+
 }; // namespace android
diff --git a/services/audioflinger/Effects.h b/services/audioflinger/Effects.h
index 0b7fb83..bac50f2 100644
--- a/services/audioflinger/Effects.h
+++ b/services/audioflinger/Effects.h
@@ -303,6 +303,10 @@
 
     void clearInputBuffer();
 
+    // At least one effect in the chain is enabled
+    bool isEnabled();
+
+
     void dump(int fd, const Vector<String16>& args);
 
 protected:
diff --git a/services/audioflinger/Threads.cpp b/services/audioflinger/Threads.cpp
index fda4211..e35f47e 100644
--- a/services/audioflinger/Threads.cpp
+++ b/services/audioflinger/Threads.cpp
@@ -939,8 +939,8 @@
         mBytesRemaining(0),
         mCurrentWriteLength(0),
         mUseAsyncWrite(false),
-        mWriteBlocked(false),
-        mDraining(false),
+        mWriteAckSequence(0),
+        mDrainSequence(0),
         mScreenState(AudioFlinger::mScreenState),
         // index 0 is reserved for normal mixer's submix
         mFastTrackAvailMask(((1 << FastMixerState::kMaxFastTracks) - 1) & ~1),
@@ -1491,29 +1491,31 @@
 void AudioFlinger::PlaybackThread::writeCallback()
 {
     ALOG_ASSERT(mCallbackThread != 0);
-    mCallbackThread->setWriteBlocked(false);
+    mCallbackThread->resetWriteBlocked();
 }
 
 void AudioFlinger::PlaybackThread::drainCallback()
 {
     ALOG_ASSERT(mCallbackThread != 0);
-    mCallbackThread->setDraining(false);
+    mCallbackThread->resetDraining();
 }
 
-void AudioFlinger::PlaybackThread::setWriteBlocked(bool value)
+void AudioFlinger::PlaybackThread::resetWriteBlocked(uint32_t sequence)
 {
     Mutex::Autolock _l(mLock);
-    mWriteBlocked = value;
-    if (!value) {
+    // reject out of sequence requests
+    if ((mWriteAckSequence & 1) && (sequence == mWriteAckSequence)) {
+        mWriteAckSequence &= ~1;
         mWaitWorkCV.signal();
     }
 }
 
-void AudioFlinger::PlaybackThread::setDraining(bool value)
+void AudioFlinger::PlaybackThread::resetDraining(uint32_t sequence)
 {
     Mutex::Autolock _l(mLock);
-    mDraining = value;
-    if (!value) {
+    // reject out of sequence requests
+    if ((mDrainSequence & 1) && (sequence == mDrainSequence)) {
+        mDrainSequence &= ~1;
         mWaitWorkCV.signal();
     }
 }
@@ -1833,9 +1835,11 @@
         // Direct output and offload threads
         size_t offset = (mCurrentWriteLength - mBytesRemaining) / sizeof(int16_t);
         if (mUseAsyncWrite) {
-            mWriteBlocked = true;
+            ALOGW_IF(mWriteAckSequence & 1, "threadLoop_write(): out of sequence write request");
+            mWriteAckSequence += 2;
+            mWriteAckSequence |= 1;
             ALOG_ASSERT(mCallbackThread != 0);
-            mCallbackThread->setWriteBlocked(true);
+            mCallbackThread->setWriteBlocked(mWriteAckSequence);
         }
         // FIXME We should have an implementation of timestamps for direct output threads.
         // They are used e.g for multichannel PCM playback over HDMI.
@@ -1844,9 +1848,9 @@
         if (mUseAsyncWrite &&
                 ((bytesWritten < 0) || (bytesWritten == (ssize_t)mBytesRemaining))) {
             // do not wait for async callback in case of error of full write
-            mWriteBlocked = false;
+            mWriteAckSequence &= ~1;
             ALOG_ASSERT(mCallbackThread != 0);
-            mCallbackThread->setWriteBlocked(false);
+            mCallbackThread->setWriteBlocked(mWriteAckSequence);
         }
     }
 
@@ -1861,9 +1865,10 @@
     if (mOutput->stream->drain) {
         ALOGV("draining %s", (mMixerStatus == MIXER_DRAIN_TRACK) ? "early" : "full");
         if (mUseAsyncWrite) {
-            mDraining = true;
+            ALOGW_IF(mDrainSequence & 1, "threadLoop_drain(): out of sequence drain request");
+            mDrainSequence |= 1;
             ALOG_ASSERT(mCallbackThread != 0);
-            mCallbackThread->setDraining(true);
+            mCallbackThread->setDraining(mDrainSequence);
         }
         mOutput->stream->drain(mOutput->stream,
             (mMixerStatus == MIXER_DRAIN_TRACK) ? AUDIO_DRAIN_EARLY_NOTIFY
@@ -2613,11 +2618,12 @@
     ALOGV("Audio hardware entering standby, mixer %p, suspend count %d", this, mSuspended);
     mOutput->stream->common.standby(&mOutput->stream->common);
     if (mUseAsyncWrite != 0) {
-        mWriteBlocked = false;
-        mDraining = false;
+        // discard any pending drain or write ack by incrementing sequence
+        mWriteAckSequence = (mWriteAckSequence + 2) & ~1;
+        mDrainSequence = (mDrainSequence + 2) & ~1;
         ALOG_ASSERT(mCallbackThread != 0);
-        mCallbackThread->setWriteBlocked(false);
-        mCallbackThread->setDraining(false);
+        mCallbackThread->setWriteBlocked(mWriteAckSequence);
+        mCallbackThread->setDraining(mDrainSequence);
     }
 }
 
@@ -3704,8 +3710,8 @@
         const sp<AudioFlinger::OffloadThread>& offloadThread)
     :   Thread(false /*canCallJava*/),
         mOffloadThread(offloadThread),
-        mWriteBlocked(false),
-        mDraining(false)
+        mWriteAckSequence(0),
+        mDrainSequence(0)
 {
 }
 
@@ -3721,8 +3727,8 @@
 bool AudioFlinger::AsyncCallbackThread::threadLoop()
 {
     while (!exitPending()) {
-        bool writeBlocked;
-        bool draining;
+        uint32_t writeAckSequence;
+        uint32_t drainSequence;
 
         {
             Mutex::Autolock _l(mLock);
@@ -3730,18 +3736,21 @@
             if (exitPending()) {
                 break;
             }
-            writeBlocked = mWriteBlocked;
-            draining = mDraining;
-            ALOGV("AsyncCallbackThread mWriteBlocked %d mDraining %d", mWriteBlocked, mDraining);
+            ALOGV("AsyncCallbackThread mWriteAckSequence %d mDrainSequence %d",
+                  mWriteAckSequence, mDrainSequence);
+            writeAckSequence = mWriteAckSequence;
+            mWriteAckSequence &= ~1;
+            drainSequence = mDrainSequence;
+            mDrainSequence &= ~1;
         }
         {
             sp<AudioFlinger::OffloadThread> offloadThread = mOffloadThread.promote();
             if (offloadThread != 0) {
-                if (writeBlocked == false) {
-                    offloadThread->setWriteBlocked(false);
+                if (writeAckSequence & 1) {
+                    offloadThread->resetWriteBlocked(writeAckSequence >> 1);
                 }
-                if (draining == false) {
-                    offloadThread->setDraining(false);
+                if (drainSequence & 1) {
+                    offloadThread->resetDraining(drainSequence >> 1);
                 }
             }
         }
@@ -3757,20 +3766,36 @@
     mWaitWorkCV.broadcast();
 }
 
-void AudioFlinger::AsyncCallbackThread::setWriteBlocked(bool value)
+void AudioFlinger::AsyncCallbackThread::setWriteBlocked(uint32_t sequence)
 {
     Mutex::Autolock _l(mLock);
-    mWriteBlocked = value;
-    if (!value) {
+    // bit 0 is cleared
+    mWriteAckSequence = sequence << 1;
+}
+
+void AudioFlinger::AsyncCallbackThread::resetWriteBlocked()
+{
+    Mutex::Autolock _l(mLock);
+    // ignore unexpected callbacks
+    if (mWriteAckSequence & 2) {
+        mWriteAckSequence |= 1;
         mWaitWorkCV.signal();
     }
 }
 
-void AudioFlinger::AsyncCallbackThread::setDraining(bool value)
+void AudioFlinger::AsyncCallbackThread::setDraining(uint32_t sequence)
 {
     Mutex::Autolock _l(mLock);
-    mDraining = value;
-    if (!value) {
+    // bit 0 is cleared
+    mDrainSequence = sequence << 1;
+}
+
+void AudioFlinger::AsyncCallbackThread::resetDraining()
+{
+    Mutex::Autolock _l(mLock);
+    // ignore unexpected callbacks
+    if (mDrainSequence & 2) {
+        mDrainSequence |= 1;
         mWaitWorkCV.signal();
     }
 }
@@ -3858,7 +3883,7 @@
             }
             tracksToRemove->add(track);
         } else if (track->framesReady() && track->isReady() &&
-                !track->isPaused() && !track->isTerminated()) {
+                !track->isPaused() && !track->isTerminated() && !track->isStopping_2()) {
             ALOGVV("OffloadThread: track %d s=%08x [OK]", track->name(), cblk->mServer);
             if (track->mFillingUpStatus == Track::FS_FILLED) {
                 track->mFillingUpStatus = Track::FS_ACTIVE;
@@ -3901,6 +3926,7 @@
                     standbyTime = systemTime() + standbyDelay;
                     if (last) {
                         mixerStatus = MIXER_DRAIN_TRACK;
+                        mDrainSequence += 2;
                         if (mHwPaused) {
                             // It is possible to move from PAUSED to STOPPING_1 without
                             // a resume so we must ensure hardware is running
@@ -3911,7 +3937,7 @@
                 }
             } else if (track->isStopping_2()) {
                 // Drain has completed, signal presentation complete
-                if (!mDraining || !last) {
+                if (!(mDrainSequence & 1) || !last) {
                     track->mState = TrackBase::STOPPED;
                     size_t audioHALFrames =
                             (mOutput->stream->get_latency(mOutput->stream)*mSampleRate) / 1000;
@@ -3956,8 +3982,9 @@
 // must be called with thread mutex locked
 bool AudioFlinger::OffloadThread::waitingAsyncCallback_l()
 {
-    ALOGV("waitingAsyncCallback_l mWriteBlocked %d mDraining %d", mWriteBlocked, mDraining);
-    if (mUseAsyncWrite && (mWriteBlocked || mDraining)) {
+    ALOGVV("waitingAsyncCallback_l mWriteAckSequence %d mDrainSequence %d",
+          mWriteAckSequence, mDrainSequence);
+    if (mUseAsyncWrite && ((mWriteAckSequence & 1) || (mDrainSequence & 1))) {
         return true;
     }
     return false;
@@ -3993,11 +4020,12 @@
     mPausedWriteLength = 0;
     mPausedBytesRemaining = 0;
     if (mUseAsyncWrite) {
-        mWriteBlocked = false;
-        mDraining = false;
+        // discard any pending drain or write ack by incrementing sequence
+        mWriteAckSequence = (mWriteAckSequence + 2) & ~1;
+        mDrainSequence = (mDrainSequence + 2) & ~1;
         ALOG_ASSERT(mCallbackThread != 0);
-        mCallbackThread->setWriteBlocked(false);
-        mCallbackThread->setDraining(false);
+        mCallbackThread->setWriteBlocked(mWriteAckSequence);
+        mCallbackThread->setDraining(mDrainSequence);
     }
 }
 
diff --git a/services/audioflinger/Threads.h b/services/audioflinger/Threads.h
index 1333de2..3fe470c 100644
--- a/services/audioflinger/Threads.h
+++ b/services/audioflinger/Threads.h
@@ -377,9 +377,9 @@
                 void        removeTracks_l(const Vector< sp<Track> >& tracksToRemove);
 
                 void        writeCallback();
-                void        setWriteBlocked(bool value);
+                void        resetWriteBlocked(uint32_t sequence);
                 void        drainCallback();
-                void        setDraining(bool value);
+                void        resetDraining(uint32_t sequence);
 
     static      int         asyncCallback(stream_callback_event_t event, void *param, void *cookie);
 
@@ -577,8 +577,19 @@
     size_t                          mBytesRemaining;
     size_t                          mCurrentWriteLength;
     bool                            mUseAsyncWrite;
-    bool                            mWriteBlocked;
-    bool                            mDraining;
+    // mWriteAckSequence contains current write sequence on bits 31-1. The write sequence is
+    // incremented each time a write(), a flush() or a standby() occurs.
+    // Bit 0 is set when a write blocks and indicates a callback is expected.
+    // Bit 0 is reset by the async callback thread calling resetWriteBlocked(). Out of sequence
+    // callbacks are ignored.
+    uint32_t                        mWriteAckSequence;
+    // mDrainSequence contains current drain sequence on bits 31-1. The drain sequence is
+    // incremented each time a drain is requested or a flush() or standby() occurs.
+    // Bit 0 is set when the drain() command is called at the HAL and indicates a callback is
+    // expected.
+    // Bit 0 is reset by the async callback thread calling resetDraining(). Out of sequence
+    // callbacks are ignored.
+    uint32_t                        mDrainSequence;
     bool                            mSignalPending;
     sp<AsyncCallbackThread>         mCallbackThread;
 
@@ -755,13 +766,21 @@
     virtual void        onFirstRef();
 
             void        exit();
-            void        setWriteBlocked(bool value);
-            void        setDraining(bool value);
+            void        setWriteBlocked(uint32_t sequence);
+            void        resetWriteBlocked();
+            void        setDraining(uint32_t sequence);
+            void        resetDraining();
 
 private:
     wp<OffloadThread>   mOffloadThread;
-    bool                mWriteBlocked;
-    bool                mDraining;
+    // mWriteAckSequence corresponds to the last write sequence passed by the offload thread via
+    // setWriteBlocked(). The sequence is shifted one bit to the left and the lsb is used
+    // to indicate that the callback has been received via resetWriteBlocked()
+    uint32_t            mWriteAckSequence;
+    // mDrainSequence corresponds to the last drain sequence passed by the offload thread via
+    // setDraining(). The sequence is shifted one bit to the left and the lsb is used
+    // to indicate that the callback has been received via resetDraining()
+    uint32_t            mDrainSequence;
     Condition           mWaitWorkCV;
     Mutex               mLock;
 };
diff --git a/services/audioflinger/Tracks.cpp b/services/audioflinger/Tracks.cpp
index 2042050..6002aa3 100644
--- a/services/audioflinger/Tracks.cpp
+++ b/services/audioflinger/Tracks.cpp
@@ -543,7 +543,17 @@
 
     sp<ThreadBase> thread = mThread.promote();
     if (thread != 0) {
-        Mutex::Autolock _l(thread->mLock);
+        //TODO: remove when effect offload is implemented
+        if (isOffloaded()) {
+            Mutex::Autolock _laf(thread->mAudioFlinger->mLock);
+            Mutex::Autolock _lth(thread->mLock);
+            sp<EffectChain> ec = thread->getEffectChain_l(mSessionId);
+            if (thread->mAudioFlinger->isGlobalEffectEnabled_l() || (ec != 0 && ec->isEnabled())) {
+                invalidate();
+                return PERMISSION_DENIED;
+            }
+        }
+        Mutex::Autolock _lth(thread->mLock);
         track_state state = mState;
         // here the track could be either new, or restarted
         // in both cases "unstop" the track
diff --git a/services/camera/libcameraservice/api1/Camera2Client.cpp b/services/camera/libcameraservice/api1/Camera2Client.cpp
index 0a18501..bda2887 100644
--- a/services/camera/libcameraservice/api1/Camera2Client.cpp
+++ b/services/camera/libcameraservice/api1/Camera2Client.cpp
@@ -743,7 +743,7 @@
         return res;
     }
 
-    Vector<uint8_t> outputStreams;
+    Vector<int32_t> outputStreams;
     bool callbacksEnabled = (params.previewCallbackFlags &
             CAMERA_FRAME_CALLBACK_FLAG_ENABLE_MASK) ||
             params.previewCallbackSurface;
@@ -999,7 +999,7 @@
         return res;
     }
 
-    Vector<uint8_t> outputStreams;
+    Vector<int32_t> outputStreams;
     outputStreams.push(getPreviewStreamId());
     outputStreams.push(getRecordingStreamId());
 
diff --git a/services/camera/libcameraservice/api1/client2/CaptureSequencer.cpp b/services/camera/libcameraservice/api1/client2/CaptureSequencer.cpp
index ad1590a..ca3198f 100644
--- a/services/camera/libcameraservice/api1/client2/CaptureSequencer.cpp
+++ b/services/camera/libcameraservice/api1/client2/CaptureSequencer.cpp
@@ -437,7 +437,8 @@
     status_t res;
     ATRACE_CALL();
     SharedParameters::Lock l(client->getParameters());
-    Vector<uint8_t> outputStreams;
+    Vector<int32_t> outputStreams;
+    uint8_t captureIntent = static_cast<uint8_t>(ANDROID_CONTROL_CAPTURE_INTENT_STILL_CAPTURE);
 
     /**
      * Set up output streams in the request
@@ -456,6 +457,7 @@
 
     if (l.mParameters.state == Parameters::VIDEO_SNAPSHOT) {
         outputStreams.push(client->getRecordingStreamId());
+        captureIntent = static_cast<uint8_t>(ANDROID_CONTROL_CAPTURE_INTENT_VIDEO_SNAPSHOT);
     }
 
     res = mCaptureRequest.update(ANDROID_REQUEST_OUTPUT_STREAMS,
@@ -465,6 +467,10 @@
                 &mCaptureId, 1);
     }
     if (res == OK) {
+        res = mCaptureRequest.update(ANDROID_CONTROL_CAPTURE_INTENT,
+                &captureIntent, 1);
+    }
+    if (res == OK) {
         res = mCaptureRequest.sort();
     }
 
diff --git a/services/camera/libcameraservice/api1/client2/StreamingProcessor.cpp b/services/camera/libcameraservice/api1/client2/StreamingProcessor.cpp
index dfe8580..6076dae 100644
--- a/services/camera/libcameraservice/api1/client2/StreamingProcessor.cpp
+++ b/services/camera/libcameraservice/api1/client2/StreamingProcessor.cpp
@@ -412,7 +412,7 @@
 }
 
 status_t StreamingProcessor::startStream(StreamType type,
-        const Vector<uint8_t> &outputStreams) {
+        const Vector<int32_t> &outputStreams) {
     ATRACE_CALL();
     status_t res;
 
@@ -830,8 +830,8 @@
     mRecordingHeapFree = mRecordingHeapCount;
 }
 
-bool StreamingProcessor::isStreamActive(const Vector<uint8_t> &streams,
-        uint8_t recordingStreamId) {
+bool StreamingProcessor::isStreamActive(const Vector<int32_t> &streams,
+        int32_t recordingStreamId) {
     for (size_t i = 0; i < streams.size(); i++) {
         if (streams[i] == recordingStreamId) {
             return true;
diff --git a/services/camera/libcameraservice/api1/client2/StreamingProcessor.h b/services/camera/libcameraservice/api1/client2/StreamingProcessor.h
index d879b83..833bb8f 100644
--- a/services/camera/libcameraservice/api1/client2/StreamingProcessor.h
+++ b/services/camera/libcameraservice/api1/client2/StreamingProcessor.h
@@ -64,7 +64,7 @@
         RECORD
     };
     status_t startStream(StreamType type,
-            const Vector<uint8_t> &outputStreams);
+            const Vector<int32_t> &outputStreams);
 
     // Toggle between paused and unpaused. Stream must be started first.
     status_t togglePauseStream(bool pause);
@@ -97,7 +97,7 @@
     StreamType mActiveRequest;
     bool mPaused;
 
-    Vector<uint8_t> mActiveStreamIds;
+    Vector<int32_t> mActiveStreamIds;
 
     // Preview-related members
     int32_t mPreviewRequestId;
@@ -132,8 +132,8 @@
     void releaseAllRecordingFramesLocked();
 
     // Determine if the specified stream is currently in use
-    static bool isStreamActive(const Vector<uint8_t> &streams,
-            uint8_t recordingStreamId);
+    static bool isStreamActive(const Vector<int32_t> &streams,
+            int32_t recordingStreamId);
 };
 
 
diff --git a/services/camera/libcameraservice/api1/client2/ZslProcessor.cpp b/services/camera/libcameraservice/api1/client2/ZslProcessor.cpp
index 3b118f4..08ab357 100644
--- a/services/camera/libcameraservice/api1/client2/ZslProcessor.cpp
+++ b/services/camera/libcameraservice/api1/client2/ZslProcessor.cpp
@@ -300,12 +300,12 @@
         uint8_t requestType = ANDROID_REQUEST_TYPE_REPROCESS;
         res = request.update(ANDROID_REQUEST_TYPE,
                 &requestType, 1);
-        uint8_t inputStreams[1] =
-                { static_cast<uint8_t>(mZslReprocessStreamId) };
+        int32_t inputStreams[1] =
+                { mZslReprocessStreamId };
         if (res == OK) request.update(ANDROID_REQUEST_INPUT_STREAMS,
                 inputStreams, 1);
-        uint8_t outputStreams[1] =
-                { static_cast<uint8_t>(client->getCaptureStreamId()) };
+        int32_t outputStreams[1] =
+                { client->getCaptureStreamId() };
         if (res == OK) request.update(ANDROID_REQUEST_OUTPUT_STREAMS,
                 outputStreams, 1);
         res = request.update(ANDROID_REQUEST_ID,
diff --git a/services/camera/libcameraservice/api1/client2/ZslProcessor3.cpp b/services/camera/libcameraservice/api1/client2/ZslProcessor3.cpp
index 7c4da50..3e05091 100644
--- a/services/camera/libcameraservice/api1/client2/ZslProcessor3.cpp
+++ b/services/camera/libcameraservice/api1/client2/ZslProcessor3.cpp
@@ -247,13 +247,13 @@
         uint8_t requestType = ANDROID_REQUEST_TYPE_REPROCESS;
         res = request.update(ANDROID_REQUEST_TYPE,
                 &requestType, 1);
-        uint8_t inputStreams[1] =
-                { static_cast<uint8_t>(mZslStreamId) };
+        int32_t inputStreams[1] =
+                { mZslStreamId };
         if (res == OK) request.update(ANDROID_REQUEST_INPUT_STREAMS,
                 inputStreams, 1);
         // TODO: Shouldn't we also update the latest preview frame?
-        uint8_t outputStreams[1] =
-                { static_cast<uint8_t>(client->getCaptureStreamId()) };
+        int32_t outputStreams[1] =
+                { client->getCaptureStreamId() };
         if (res == OK) request.update(ANDROID_REQUEST_OUTPUT_STREAMS,
                 outputStreams, 1);
         res = request.update(ANDROID_REQUEST_ID,
diff --git a/services/camera/libcameraservice/api2/CameraDeviceClient.cpp b/services/camera/libcameraservice/api2/CameraDeviceClient.cpp
index f147c06..055ea12 100644
--- a/services/camera/libcameraservice/api2/CameraDeviceClient.cpp
+++ b/services/camera/libcameraservice/api2/CameraDeviceClient.cpp
@@ -137,7 +137,7 @@
      * Write in the output stream IDs which we calculate from
      * the capture request's list of surface targets
      */
-    Vector<uint8_t> outputStreamIds;
+    Vector<int32_t> outputStreamIds;
     outputStreamIds.setCapacity(request->mSurfaceList.size());
     for (size_t i = 0; i < request->mSurfaceList.size(); ++i) {
         sp<Surface> surface = request->mSurfaceList[i];
diff --git a/services/camera/libcameraservice/device3/Camera3Device.cpp b/services/camera/libcameraservice/device3/Camera3Device.cpp
index 47321e0..b70a278 100644
--- a/services/camera/libcameraservice/device3/Camera3Device.cpp
+++ b/services/camera/libcameraservice/device3/Camera3Device.cpp
@@ -281,6 +281,14 @@
     }
     write(fd, lines.string(), lines.size());
 
+    {
+        lines = String8("    Last request sent:\n");
+        write(fd, lines.string(), lines.size());
+
+        CameraMetadata lastRequest = getLatestRequest();
+        lastRequest.dump(fd, /*verbosity*/2, /*indentation*/6);
+    }
+
     if (mHal3Device != NULL) {
         lines = String8("    HAL device dump:\n");
         write(fd, lines.string(), lines.size());
@@ -978,7 +986,7 @@
             newRequest->mSettings.find(ANDROID_REQUEST_INPUT_STREAMS);
     if (inputStreams.count > 0) {
         if (mInputStream == NULL ||
-                mInputStream->getId() != inputStreams.data.u8[0]) {
+                mInputStream->getId() != inputStreams.data.i32[0]) {
             CLOGE("Request references unknown input stream %d",
                     inputStreams.data.u8[0]);
             return NULL;
@@ -1007,7 +1015,7 @@
     }
 
     for (size_t i = 0; i < streams.count; i++) {
-        int idx = mOutputStreams.indexOfKey(streams.data.u8[i]);
+        int idx = mOutputStreams.indexOfKey(streams.data.i32[i]);
         if (idx == NAME_NOT_FOUND) {
             CLOGE("Request references unknown stream %d",
                     streams.data.u8[i]);
@@ -1397,6 +1405,43 @@
     }
 }
 
+CameraMetadata Camera3Device::getLatestRequest() {
+    ALOGV("%s", __FUNCTION__);
+
+    bool locked = false;
+
+    /**
+     * Why trylock instead of autolock?
+     *
+     * We want to be able to call this function from
+     * dumpsys, which often happens during deadlocks.
+     */
+    for (size_t i = 0; i < kDumpLockAttempts; ++i) {
+        if (mLock.tryLock() == NO_ERROR) {
+            locked = true;
+            break;
+        } else {
+            usleep(kDumpSleepDuration);
+        }
+    }
+
+    if (!locked) {
+        ALOGW("%s: Possible deadlock detected", __FUNCTION__);
+    }
+
+    CameraMetadata retVal;
+
+    if (mRequestThread != NULL) {
+        retVal = mRequestThread->getLatestRequest();
+    }
+
+    if (locked) {
+        mLock.unlock();
+    }
+
+    return retVal;
+}
+
 /**
  * RequestThread inner class methods
  */
@@ -1677,6 +1722,14 @@
         return false;
     }
 
+    // Update the latest request sent to HAL
+    if (request.settings != NULL) { // Don't update them if they were unchanged
+        Mutex::Autolock al(mLatestRequestMutex);
+
+        camera_metadata_t* cloned = clone_camera_metadata(request.settings);
+        mLatestRequest.acquire(cloned);
+    }
+
     if (request.settings != NULL) {
         nextRequest->mSettings.unlock(request.settings);
     }
@@ -1729,6 +1782,14 @@
     return true;
 }
 
+CameraMetadata Camera3Device::RequestThread::getLatestRequest() const {
+    Mutex::Autolock al(mLatestRequestMutex);
+
+    ALOGV("RequestThread::%s", __FUNCTION__);
+
+    return mLatestRequest;
+}
+
 void Camera3Device::RequestThread::cleanUpFailedRequest(
         camera3_capture_request_t &request,
         sp<CaptureRequest> &nextRequest,
diff --git a/services/camera/libcameraservice/device3/Camera3Device.h b/services/camera/libcameraservice/device3/Camera3Device.h
index 6565048..0b3ad6e 100644
--- a/services/camera/libcameraservice/device3/Camera3Device.h
+++ b/services/camera/libcameraservice/device3/Camera3Device.h
@@ -127,6 +127,8 @@
     virtual status_t flush();
 
   private:
+    static const size_t        kDumpLockAttempts  = 10;
+    static const size_t        kDumpSleepDuration = 100000; // 0.10 sec
     static const size_t        kInFlightWarnLimit = 20;
     static const nsecs_t       kShutdownTimeout   = 5000000000; // 5 sec
     struct                     RequestTrigger;
@@ -175,6 +177,13 @@
     typedef List<sp<CaptureRequest> > RequestList;
 
     /**
+     * Get the last request submitted to the hal by the request thread.
+     *
+     * Takes mLock.
+     */
+    virtual CameraMetadata getLatestRequest();
+
+    /**
      * Lock-held version of waitUntilDrained. Will transition to IDLE on
      * success.
      */
@@ -285,6 +294,12 @@
          */
         status_t waitUntilRequestProcessed(int32_t requestId, nsecs_t timeout);
 
+        /**
+         * Get the latest request that was sent to the HAL
+         * with process_capture_request.
+         */
+        CameraMetadata getLatestRequest() const;
+
       protected:
 
         virtual bool threadLoop();
@@ -343,10 +358,11 @@
 
         uint32_t           mFrameNumber;
 
-        Mutex              mLatestRequestMutex;
+        mutable Mutex      mLatestRequestMutex;
         Condition          mLatestRequestSignal;
         // android.request.id for latest process_capture_request
         int32_t            mLatestRequestId;
+        CameraMetadata     mLatestRequest;
 
         typedef KeyedVector<uint32_t/*tag*/, RequestTrigger> TriggerMap;
         Mutex              mTriggerMutex;