Merge "Fixed that DcTracker does not use correct data registration info"
diff --git a/core/jni/android_view_Surface.cpp b/core/jni/android_view_Surface.cpp
index f70cf07..6302aa7 100644
--- a/core/jni/android_view_Surface.cpp
+++ b/core/jni/android_view_Surface.cpp
@@ -127,113 +127,17 @@
 }
 
 int android_view_Surface_mapPublicFormatToHalFormat(PublicFormat f) {
-
-    switch(f) {
-        case PublicFormat::JPEG:
-        case PublicFormat::DEPTH_POINT_CLOUD:
-            return HAL_PIXEL_FORMAT_BLOB;
-        case PublicFormat::DEPTH16:
-            return HAL_PIXEL_FORMAT_Y16;
-        case PublicFormat::RAW_SENSOR:
-        case PublicFormat::RAW_DEPTH:
-            return HAL_PIXEL_FORMAT_RAW16;
-        default:
-            // Most formats map 1:1
-            return static_cast<int>(f);
-    }
+    return mapPublicFormatToHalFormat(f);
 }
 
 android_dataspace android_view_Surface_mapPublicFormatToHalDataspace(
         PublicFormat f) {
-    switch(f) {
-        case PublicFormat::JPEG:
-            return HAL_DATASPACE_V0_JFIF;
-        case PublicFormat::DEPTH_POINT_CLOUD:
-        case PublicFormat::DEPTH16:
-        case PublicFormat::RAW_DEPTH:
-            return HAL_DATASPACE_DEPTH;
-        case PublicFormat::RAW_SENSOR:
-        case PublicFormat::RAW_PRIVATE:
-        case PublicFormat::RAW10:
-        case PublicFormat::RAW12:
-            return HAL_DATASPACE_ARBITRARY;
-        case PublicFormat::YUV_420_888:
-        case PublicFormat::NV21:
-        case PublicFormat::YV12:
-            return HAL_DATASPACE_V0_JFIF;
-        default:
-            // Most formats map to UNKNOWN
-            return HAL_DATASPACE_UNKNOWN;
-    }
+    return mapPublicFormatToHalDataspace(f);
 }
 
 PublicFormat android_view_Surface_mapHalFormatDataspaceToPublicFormat(
         int format, android_dataspace dataSpace) {
-    switch(format) {
-        case HAL_PIXEL_FORMAT_RGBA_8888:
-        case HAL_PIXEL_FORMAT_RGBX_8888:
-        case HAL_PIXEL_FORMAT_RGBA_FP16:
-        case HAL_PIXEL_FORMAT_RGBA_1010102:
-        case HAL_PIXEL_FORMAT_RGB_888:
-        case HAL_PIXEL_FORMAT_RGB_565:
-        case HAL_PIXEL_FORMAT_Y8:
-        case HAL_PIXEL_FORMAT_RAW10:
-        case HAL_PIXEL_FORMAT_RAW12:
-        case HAL_PIXEL_FORMAT_YCbCr_420_888:
-        case HAL_PIXEL_FORMAT_YV12:
-            // Enums overlap in both name and value
-            return static_cast<PublicFormat>(format);
-        case HAL_PIXEL_FORMAT_RAW16:
-            switch (dataSpace) {
-                case HAL_DATASPACE_DEPTH:
-                  return PublicFormat::RAW_DEPTH;
-                default:
-                  return PublicFormat::RAW_SENSOR;
-            }
-        case HAL_PIXEL_FORMAT_RAW_OPAQUE:
-            // Name differs, though value is the same
-            return PublicFormat::RAW_PRIVATE;
-        case HAL_PIXEL_FORMAT_YCbCr_422_SP:
-            // Name differs, though the value is the same
-            return PublicFormat::NV16;
-        case HAL_PIXEL_FORMAT_YCrCb_420_SP:
-            // Name differs, though the value is the same
-            return PublicFormat::NV21;
-        case HAL_PIXEL_FORMAT_YCbCr_422_I:
-            // Name differs, though the value is the same
-            return PublicFormat::YUY2;
-        case HAL_PIXEL_FORMAT_IMPLEMENTATION_DEFINED:
-            // Name differs, though the value is the same
-            return PublicFormat::PRIVATE;
-        case HAL_PIXEL_FORMAT_Y16:
-            // Dataspace-dependent
-            switch (dataSpace) {
-                case HAL_DATASPACE_DEPTH:
-                    return PublicFormat::DEPTH16;
-                default:
-                    // Assume non-depth Y16 is just Y16.
-                    return PublicFormat::Y16;
-            }
-            break;
-        case HAL_PIXEL_FORMAT_BLOB:
-            // Dataspace-dependent
-            switch (dataSpace) {
-                case HAL_DATASPACE_DEPTH:
-                    return PublicFormat::DEPTH_POINT_CLOUD;
-                case HAL_DATASPACE_V0_JFIF:
-                    return PublicFormat::JPEG;
-                default:
-                    // Assume otherwise-marked blobs are also JPEG
-                    return PublicFormat::JPEG;
-            }
-            break;
-        case HAL_PIXEL_FORMAT_BGRA_8888:
-            // Not defined in public API
-            return PublicFormat::UNKNOWN;
-
-        default:
-            return PublicFormat::UNKNOWN;
-    }
+    return mapHalFormatDataspaceToPublicFormat(format, dataSpace);
 }
 // ----------------------------------------------------------------------------
 
diff --git a/core/jni/include/android_runtime/android_view_Surface.h b/core/jni/include/android_runtime/android_view_Surface.h
index 36487b3..04718cd 100644
--- a/core/jni/include/android_runtime/android_view_Surface.h
+++ b/core/jni/include/android_runtime/android_view_Surface.h
@@ -19,6 +19,7 @@
 
 #include <android/native_window.h>
 #include <system/graphics.h>
+#include <ui/PublicFormat.h>
 
 #include "jni.h"
 
@@ -27,39 +28,6 @@
 class Surface;
 class IGraphicBufferProducer;
 
-/**
- * Enum mirroring the public API definitions for image and pixel formats.
- * Some of these are hidden in the public API
- *
- * Keep up to date with android.graphics.ImageFormat and
- * android.graphics.PixelFormat
- */
-enum class PublicFormat {
-    UNKNOWN           = 0x0,
-    RGBA_8888         = 0x1,
-    RGBX_8888         = 0x2,
-    RGB_888           = 0x3,
-    RGB_565           = 0x4,
-    NV16              = 0x10,
-    NV21              = 0x11,
-    YUY2              = 0x14,
-    RGBA_FP16         = 0x16,
-    RAW_SENSOR        = 0x20,
-    PRIVATE           = 0x22,
-    YUV_420_888       = 0x23,
-    RAW_PRIVATE       = 0x24,
-    RAW10             = 0x25,
-    RAW12             = 0x26,
-    RGBA_1010102      = 0x2b,
-    JPEG              = 0x100,
-    DEPTH_POINT_CLOUD = 0x101,
-    RAW_DEPTH         = 0x1002, // @hide
-    YV12              = 0x32315659,
-    Y8                = 0x20203859, // @hide
-    Y16               = 0x20363159, // @hide
-    DEPTH16           = 0x44363159
-};
-
 /* Gets the underlying ANativeWindow for a Surface. */
 extern sp<ANativeWindow> android_view_Surface_getNativeWindow(
         JNIEnv* env, jobject surfaceObj);
diff --git a/media/jni/Android.bp b/media/jni/Android.bp
index 25c7b5c..5966192 100644
--- a/media/jni/Android.bp
+++ b/media/jni/Android.bp
@@ -20,8 +20,8 @@
         "android_media_MediaScanner.cpp",
         "android_media_MediaSync.cpp",
         "android_media_ResampleInputStream.cpp",
+        "android_media_Streams.cpp",
         "android_media_SyncParams.cpp",
-        "android_media_Utils.cpp",
         "android_mtp_MtpDatabase.cpp",
         "android_mtp_MtpDevice.cpp",
         "android_mtp_MtpServer.cpp",
@@ -36,6 +36,7 @@
         "libbinder",
         "libmedia",
         "libmediaextractor",
+        "libmedia_jni_utils",
         "libmedia_omx",
         "libmediametrics",
         "libmediadrm",
@@ -91,6 +92,36 @@
 }
 
 cc_library_shared {
+    name: "libmedia_jni_utils",
+    srcs: [
+        "android_media_Utils.cpp",
+    ],
+
+    shared_libs: [
+        "liblog",
+        "libmedia_omx",
+        "libnativewindow",
+        "libui",
+        "libutils",
+        "android.hidl.token@1.0-utils",
+    ],
+
+    include_dirs: [
+        "system/media/camera/include",
+    ],
+
+    export_include_dirs: ["."],
+
+    cflags: [
+        "-Wall",
+        "-Werror",
+        "-Wno-error=deprecated-declarations",
+        "-Wunused",
+        "-Wunreachable-code",
+    ],
+}
+
+cc_library_shared {
     name: "libmedia2_jni",
 
     srcs: [
diff --git a/media/jni/android_media_MediaCodec.cpp b/media/jni/android_media_MediaCodec.cpp
index 3490ff8..cf73b21 100644
--- a/media/jni/android_media_MediaCodec.cpp
+++ b/media/jni/android_media_MediaCodec.cpp
@@ -23,7 +23,7 @@
 #include "android_media_MediaCrypto.h"
 #include "android_media_MediaDescrambler.h"
 #include "android_media_MediaMetricsJNI.h"
-#include "android_media_Utils.h"
+#include "android_media_Streams.h"
 #include "android_runtime/AndroidRuntime.h"
 #include "android_runtime/android_view_Surface.h"
 #include "android_util_Binder.h"
diff --git a/media/jni/android_media_MediaCodecList.cpp b/media/jni/android_media_MediaCodecList.cpp
index 8de11ca..878910a 100644
--- a/media/jni/android_media_MediaCodecList.cpp
+++ b/media/jni/android_media_MediaCodecList.cpp
@@ -27,7 +27,7 @@
 #include "android_runtime/AndroidRuntime.h"
 #include "jni.h"
 #include <nativehelper/JNIHelp.h>
-#include "android_media_Utils.h"
+#include "android_media_Streams.h"
 
 using namespace android;
 
diff --git a/media/jni/android_media_MediaExtractor.cpp b/media/jni/android_media_MediaExtractor.cpp
index dad0e53..3edac44 100644
--- a/media/jni/android_media_MediaExtractor.cpp
+++ b/media/jni/android_media_MediaExtractor.cpp
@@ -21,7 +21,7 @@
 #include "android_media_MediaDataSource.h"
 #include "android_media_MediaExtractor.h"
 #include "android_media_MediaMetricsJNI.h"
-#include "android_media_Utils.h"
+#include "android_media_Streams.h"
 #include "android_os_HwRemoteBinder.h"
 #include "android_runtime/AndroidRuntime.h"
 #include "android_runtime/Log.h"
diff --git a/media/jni/android_media_MediaMetadataRetriever.cpp b/media/jni/android_media_MediaMetadataRetriever.cpp
index c1226fa..a480784 100644
--- a/media/jni/android_media_MediaMetadataRetriever.cpp
+++ b/media/jni/android_media_MediaMetadataRetriever.cpp
@@ -18,6 +18,7 @@
 //#define LOG_NDEBUG 0
 #define LOG_TAG "MediaMetadataRetrieverJNI"
 
+#include <cmath>
 #include <assert.h>
 #include <utils/Log.h>
 #include <utils/threads.h>
@@ -32,7 +33,7 @@
 #include <nativehelper/JNIHelp.h>
 #include "android_runtime/AndroidRuntime.h"
 #include "android_media_MediaDataSource.h"
-#include "android_media_Utils.h"
+#include "android_media_Streams.h"
 #include "android_util_Binder.h"
 
 #include "android/graphics/GraphicsJNI.h"
diff --git a/media/jni/android_media_MediaMuxer.cpp b/media/jni/android_media_MediaMuxer.cpp
index f11452a..f0aa4c3 100644
--- a/media/jni/android_media_MediaMuxer.cpp
+++ b/media/jni/android_media_MediaMuxer.cpp
@@ -18,7 +18,7 @@
 #define LOG_TAG "MediaMuxer-JNI"
 #include <utils/Log.h>
 
-#include "android_media_Utils.h"
+#include "android_media_Streams.h"
 #include "android_runtime/AndroidRuntime.h"
 #include "jni.h"
 #include <nativehelper/JNIHelp.h>
diff --git a/media/jni/android_media_MediaPlayer.cpp b/media/jni/android_media_MediaPlayer.cpp
index 5dd01b0..2acb0e5 100644
--- a/media/jni/android_media_MediaPlayer.cpp
+++ b/media/jni/android_media_MediaPlayer.cpp
@@ -45,7 +45,7 @@
 #include "android_media_PlaybackParams.h"
 #include "android_media_SyncParams.h"
 #include "android_media_VolumeShaper.h"
-#include "android_media_Utils.h"
+#include "android_media_Streams.h"
 
 #include "android_os_Parcel.h"
 #include "android_util_Binder.h"
diff --git a/media/jni/android_media_Streams.cpp b/media/jni/android_media_Streams.cpp
new file mode 100644
index 0000000..b7cbd97
--- /dev/null
+++ b/media/jni/android_media_Streams.cpp
@@ -0,0 +1,559 @@
+/*
+ * Copyright 2019, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+// #define LOG_NDEBUG 0
+#define LOG_TAG "AndroidMediaStreams"
+
+#include <utils/Log.h>
+#include "android_media_Streams.h"
+
+#include <media/stagefright/foundation/ADebug.h>
+#include <media/stagefright/foundation/ABuffer.h>
+#include <media/stagefright/foundation/AMessage.h>
+
+#include <nativehelper/ScopedLocalRef.h>
+
+namespace android {
+
+AssetStream::AssetStream(SkStream* stream)
+    : mStream(stream), mPosition(0) {
+}
+
+AssetStream::~AssetStream() {
+}
+
+piex::Error AssetStream::GetData(
+        const size_t offset, const size_t length, std::uint8_t* data) {
+    // Seek first.
+    if (mPosition != offset) {
+        if (!mStream->seek(offset)) {
+            return piex::Error::kFail;
+        }
+    }
+
+    // Read bytes.
+    size_t size = mStream->read((void*)data, length);
+    mPosition = offset + size;
+
+    return size == length ? piex::Error::kOk : piex::Error::kFail;
+}
+
+BufferedStream::BufferedStream(SkStream* stream)
+    : mStream(stream) {
+}
+
+BufferedStream::~BufferedStream() {
+}
+
+piex::Error BufferedStream::GetData(
+        const size_t offset, const size_t length, std::uint8_t* data) {
+    // Seek first.
+    if (offset + length > mStreamBuffer.bytesWritten()) {
+        size_t sizeToRead = offset + length - mStreamBuffer.bytesWritten();
+        if (sizeToRead <= kMinSizeToRead) {
+            sizeToRead = kMinSizeToRead;
+        }
+
+        void* tempBuffer = malloc(sizeToRead);
+        if (tempBuffer == NULL) {
+          return piex::Error::kFail;
+        }
+
+        size_t bytesRead = mStream->read(tempBuffer, sizeToRead);
+        if (bytesRead != sizeToRead) {
+            free(tempBuffer);
+            return piex::Error::kFail;
+        }
+        mStreamBuffer.write(tempBuffer, bytesRead);
+        free(tempBuffer);
+    }
+
+    // Read bytes.
+    if (mStreamBuffer.read((void*)data, offset, length)) {
+        return piex::Error::kOk;
+    } else {
+        return piex::Error::kFail;
+    }
+}
+
+FileStream::FileStream(const int fd)
+    : mPosition(0) {
+    mFile = fdopen(fd, "r");
+    if (mFile == NULL) {
+        return;
+    }
+}
+
+FileStream::FileStream(const String8 filename)
+    : mPosition(0) {
+    mFile = fopen(filename.string(), "r");
+    if (mFile == NULL) {
+        return;
+    }
+}
+
+FileStream::~FileStream() {
+    if (mFile != NULL) {
+        fclose(mFile);
+        mFile = NULL;
+    }
+}
+
+piex::Error FileStream::GetData(
+        const size_t offset, const size_t length, std::uint8_t* data) {
+    if (mFile == NULL) {
+        return piex::Error::kFail;
+    }
+
+    // Seek first.
+    if (mPosition != offset) {
+        fseek(mFile, offset, SEEK_SET);
+    }
+
+    // Read bytes.
+    size_t size = fread((void*)data, sizeof(std::uint8_t), length, mFile);
+    mPosition += size;
+
+    // Handle errors and verify the size.
+    if (ferror(mFile) || size != length) {
+        ALOGV("GetData read failed: (offset: %zu, length: %zu)", offset, length);
+        return piex::Error::kFail;
+    }
+    return piex::Error::kOk;
+}
+
+bool FileStream::exists() const {
+    return mFile != NULL;
+}
+
+bool GetExifFromRawImage(
+        piex::StreamInterface* stream, const String8& filename,
+        piex::PreviewImageData& image_data) {
+    // Reset the PreviewImageData to its default.
+    image_data = piex::PreviewImageData();
+
+    if (!piex::IsRaw(stream)) {
+        // Format not supported.
+        ALOGV("Format not supported: %s", filename.string());
+        return false;
+    }
+
+    piex::Error err = piex::GetPreviewImageData(stream, &image_data);
+
+    if (err != piex::Error::kOk) {
+        // The input data seems to be broken.
+        ALOGV("Raw image not detected: %s (piex error code: %d)", filename.string(), (int32_t)err);
+        return false;
+    }
+
+    return true;
+}
+
+bool ConvertKeyValueArraysToKeyedVector(
+        JNIEnv *env, jobjectArray keys, jobjectArray values,
+        KeyedVector<String8, String8>* keyedVector) {
+
+    int nKeyValuePairs = 0;
+    bool failed = false;
+    if (keys != NULL && values != NULL) {
+        nKeyValuePairs = env->GetArrayLength(keys);
+        failed = (nKeyValuePairs != env->GetArrayLength(values));
+    }
+
+    if (!failed) {
+        failed = ((keys != NULL && values == NULL) ||
+                  (keys == NULL && values != NULL));
+    }
+
+    if (failed) {
+        ALOGE("keys and values arrays have different length");
+        jniThrowException(env, "java/lang/IllegalArgumentException", NULL);
+        return false;
+    }
+
+    for (int i = 0; i < nKeyValuePairs; ++i) {
+        // No need to check on the ArrayIndexOutOfBoundsException, since
+        // it won't happen here.
+        jstring key = (jstring) env->GetObjectArrayElement(keys, i);
+        jstring value = (jstring) env->GetObjectArrayElement(values, i);
+
+        const char* keyStr = env->GetStringUTFChars(key, NULL);
+        if (!keyStr) {  // OutOfMemoryError
+            return false;
+        }
+
+        const char* valueStr = env->GetStringUTFChars(value, NULL);
+        if (!valueStr) {  // OutOfMemoryError
+            env->ReleaseStringUTFChars(key, keyStr);
+            return false;
+        }
+
+        keyedVector->add(String8(keyStr), String8(valueStr));
+
+        env->ReleaseStringUTFChars(key, keyStr);
+        env->ReleaseStringUTFChars(value, valueStr);
+        env->DeleteLocalRef(key);
+        env->DeleteLocalRef(value);
+    }
+    return true;
+}
+
+static jobject makeIntegerObject(JNIEnv *env, int32_t value) {
+    ScopedLocalRef<jclass> clazz(env, env->FindClass("java/lang/Integer"));
+    CHECK(clazz.get() != NULL);
+
+    jmethodID integerConstructID =
+        env->GetMethodID(clazz.get(), "<init>", "(I)V");
+    CHECK(integerConstructID != NULL);
+
+    return env->NewObject(clazz.get(), integerConstructID, value);
+}
+
+static jobject makeLongObject(JNIEnv *env, int64_t value) {
+    ScopedLocalRef<jclass> clazz(env, env->FindClass("java/lang/Long"));
+    CHECK(clazz.get() != NULL);
+
+    jmethodID longConstructID = env->GetMethodID(clazz.get(), "<init>", "(J)V");
+    CHECK(longConstructID != NULL);
+
+    return env->NewObject(clazz.get(), longConstructID, value);
+}
+
+static jobject makeFloatObject(JNIEnv *env, float value) {
+    ScopedLocalRef<jclass> clazz(env, env->FindClass("java/lang/Float"));
+    CHECK(clazz.get() != NULL);
+
+    jmethodID floatConstructID =
+        env->GetMethodID(clazz.get(), "<init>", "(F)V");
+    CHECK(floatConstructID != NULL);
+
+    return env->NewObject(clazz.get(), floatConstructID, value);
+}
+
+static jobject makeByteBufferObject(
+        JNIEnv *env, const void *data, size_t size) {
+    jbyteArray byteArrayObj = env->NewByteArray(size);
+    env->SetByteArrayRegion(byteArrayObj, 0, size, (const jbyte *)data);
+
+    ScopedLocalRef<jclass> clazz(env, env->FindClass("java/nio/ByteBuffer"));
+    CHECK(clazz.get() != NULL);
+
+    jmethodID byteBufWrapID =
+        env->GetStaticMethodID(
+                clazz.get(), "wrap", "([B)Ljava/nio/ByteBuffer;");
+    CHECK(byteBufWrapID != NULL);
+
+    jobject byteBufObj = env->CallStaticObjectMethod(
+            clazz.get(), byteBufWrapID, byteArrayObj);
+
+    env->DeleteLocalRef(byteArrayObj); byteArrayObj = NULL;
+
+    return byteBufObj;
+}
+
+static void SetMapInt32(
+        JNIEnv *env, jobject hashMapObj, jmethodID hashMapPutID,
+        const char *key, int32_t value) {
+    jstring keyObj = env->NewStringUTF(key);
+    jobject valueObj = makeIntegerObject(env, value);
+
+    env->CallObjectMethod(hashMapObj, hashMapPutID, keyObj, valueObj);
+
+    env->DeleteLocalRef(valueObj); valueObj = NULL;
+    env->DeleteLocalRef(keyObj); keyObj = NULL;
+}
+
+status_t ConvertMessageToMap(
+        JNIEnv *env, const sp<AMessage> &msg, jobject *map) {
+    ScopedLocalRef<jclass> hashMapClazz(
+            env, env->FindClass("java/util/HashMap"));
+
+    if (hashMapClazz.get() == NULL) {
+        return -EINVAL;
+    }
+
+    jmethodID hashMapConstructID =
+        env->GetMethodID(hashMapClazz.get(), "<init>", "()V");
+
+    if (hashMapConstructID == NULL) {
+        return -EINVAL;
+    }
+
+    jmethodID hashMapPutID =
+        env->GetMethodID(
+                hashMapClazz.get(),
+                "put",
+                "(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;");
+
+    if (hashMapPutID == NULL) {
+        return -EINVAL;
+    }
+
+    jobject hashMap = env->NewObject(hashMapClazz.get(), hashMapConstructID);
+
+    for (size_t i = 0; i < msg->countEntries(); ++i) {
+        AMessage::Type valueType;
+        const char *key = msg->getEntryNameAt(i, &valueType);
+
+        if (!strncmp(key, "android._", 9)) {
+            // don't expose private keys (starting with android._)
+            continue;
+        }
+
+        jobject valueObj = NULL;
+
+        switch (valueType) {
+            case AMessage::kTypeInt32:
+            {
+                int32_t val;
+                CHECK(msg->findInt32(key, &val));
+
+                valueObj = makeIntegerObject(env, val);
+                break;
+            }
+
+            case AMessage::kTypeInt64:
+            {
+                int64_t val;
+                CHECK(msg->findInt64(key, &val));
+
+                valueObj = makeLongObject(env, val);
+                break;
+            }
+
+            case AMessage::kTypeFloat:
+            {
+                float val;
+                CHECK(msg->findFloat(key, &val));
+
+                valueObj = makeFloatObject(env, val);
+                break;
+            }
+
+            case AMessage::kTypeString:
+            {
+                AString val;
+                CHECK(msg->findString(key, &val));
+
+                valueObj = env->NewStringUTF(val.c_str());
+                break;
+            }
+
+            case AMessage::kTypeBuffer:
+            {
+                sp<ABuffer> buffer;
+                CHECK(msg->findBuffer(key, &buffer));
+
+                valueObj = makeByteBufferObject(
+                        env, buffer->data(), buffer->size());
+                break;
+            }
+
+            case AMessage::kTypeRect:
+            {
+                int32_t left, top, right, bottom;
+                CHECK(msg->findRect(key, &left, &top, &right, &bottom));
+
+                SetMapInt32(
+                        env,
+                        hashMap,
+                        hashMapPutID,
+                        AStringPrintf("%s-left", key).c_str(),
+                        left);
+
+                SetMapInt32(
+                        env,
+                        hashMap,
+                        hashMapPutID,
+                        AStringPrintf("%s-top", key).c_str(),
+                        top);
+
+                SetMapInt32(
+                        env,
+                        hashMap,
+                        hashMapPutID,
+                        AStringPrintf("%s-right", key).c_str(),
+                        right);
+
+                SetMapInt32(
+                        env,
+                        hashMap,
+                        hashMapPutID,
+                        AStringPrintf("%s-bottom", key).c_str(),
+                        bottom);
+                break;
+            }
+
+            default:
+                break;
+        }
+
+        if (valueObj != NULL) {
+            jstring keyObj = env->NewStringUTF(key);
+
+            env->CallObjectMethod(hashMap, hashMapPutID, keyObj, valueObj);
+
+            env->DeleteLocalRef(keyObj); keyObj = NULL;
+            env->DeleteLocalRef(valueObj); valueObj = NULL;
+        }
+    }
+
+    *map = hashMap;
+
+    return OK;
+}
+
+status_t ConvertKeyValueArraysToMessage(
+        JNIEnv *env, jobjectArray keys, jobjectArray values,
+        sp<AMessage> *out) {
+    ScopedLocalRef<jclass> stringClass(env, env->FindClass("java/lang/String"));
+    CHECK(stringClass.get() != NULL);
+    ScopedLocalRef<jclass> integerClass(env, env->FindClass("java/lang/Integer"));
+    CHECK(integerClass.get() != NULL);
+    ScopedLocalRef<jclass> longClass(env, env->FindClass("java/lang/Long"));
+    CHECK(longClass.get() != NULL);
+    ScopedLocalRef<jclass> floatClass(env, env->FindClass("java/lang/Float"));
+    CHECK(floatClass.get() != NULL);
+    ScopedLocalRef<jclass> byteBufClass(env, env->FindClass("java/nio/ByteBuffer"));
+    CHECK(byteBufClass.get() != NULL);
+
+    sp<AMessage> msg = new AMessage;
+
+    jsize numEntries = 0;
+
+    if (keys != NULL) {
+        if (values == NULL) {
+            return -EINVAL;
+        }
+
+        numEntries = env->GetArrayLength(keys);
+
+        if (numEntries != env->GetArrayLength(values)) {
+            return -EINVAL;
+        }
+    } else if (values != NULL) {
+        return -EINVAL;
+    }
+
+    for (jsize i = 0; i < numEntries; ++i) {
+        jobject keyObj = env->GetObjectArrayElement(keys, i);
+
+        if (!env->IsInstanceOf(keyObj, stringClass.get())) {
+            return -EINVAL;
+        }
+
+        const char *tmp = env->GetStringUTFChars((jstring)keyObj, NULL);
+
+        if (tmp == NULL) {
+            return -ENOMEM;
+        }
+
+        AString key = tmp;
+
+        env->ReleaseStringUTFChars((jstring)keyObj, tmp);
+        tmp = NULL;
+
+        if (key.startsWith("android._")) {
+            // don't propagate private keys (starting with android._)
+            continue;
+        }
+
+        jobject valueObj = env->GetObjectArrayElement(values, i);
+
+        if (env->IsInstanceOf(valueObj, stringClass.get())) {
+            const char *value = env->GetStringUTFChars((jstring)valueObj, NULL);
+
+            if (value == NULL) {
+                return -ENOMEM;
+            }
+
+            msg->setString(key.c_str(), value);
+
+            env->ReleaseStringUTFChars((jstring)valueObj, value);
+            value = NULL;
+        } else if (env->IsInstanceOf(valueObj, integerClass.get())) {
+            jmethodID intValueID =
+                env->GetMethodID(integerClass.get(), "intValue", "()I");
+            CHECK(intValueID != NULL);
+
+            jint value = env->CallIntMethod(valueObj, intValueID);
+
+            msg->setInt32(key.c_str(), value);
+        } else if (env->IsInstanceOf(valueObj, longClass.get())) {
+            jmethodID longValueID =
+                env->GetMethodID(longClass.get(), "longValue", "()J");
+            CHECK(longValueID != NULL);
+
+            jlong value = env->CallLongMethod(valueObj, longValueID);
+
+            msg->setInt64(key.c_str(), value);
+        } else if (env->IsInstanceOf(valueObj, floatClass.get())) {
+            jmethodID floatValueID =
+                env->GetMethodID(floatClass.get(), "floatValue", "()F");
+            CHECK(floatValueID != NULL);
+
+            jfloat value = env->CallFloatMethod(valueObj, floatValueID);
+
+            msg->setFloat(key.c_str(), value);
+        } else if (env->IsInstanceOf(valueObj, byteBufClass.get())) {
+            jmethodID positionID =
+                env->GetMethodID(byteBufClass.get(), "position", "()I");
+            CHECK(positionID != NULL);
+
+            jmethodID limitID =
+                env->GetMethodID(byteBufClass.get(), "limit", "()I");
+            CHECK(limitID != NULL);
+
+            jint position = env->CallIntMethod(valueObj, positionID);
+            jint limit = env->CallIntMethod(valueObj, limitID);
+
+            sp<ABuffer> buffer = new ABuffer(limit - position);
+
+            void *data = env->GetDirectBufferAddress(valueObj);
+
+            if (data != NULL) {
+                memcpy(buffer->data(),
+                       (const uint8_t *)data + position,
+                       buffer->size());
+            } else {
+                jmethodID arrayID =
+                    env->GetMethodID(byteBufClass.get(), "array", "()[B");
+                CHECK(arrayID != NULL);
+
+                jbyteArray byteArray =
+                    (jbyteArray)env->CallObjectMethod(valueObj, arrayID);
+                CHECK(byteArray != NULL);
+
+                env->GetByteArrayRegion(
+                        byteArray,
+                        position,
+                        buffer->size(),
+                        (jbyte *)buffer->data());
+
+                env->DeleteLocalRef(byteArray); byteArray = NULL;
+            }
+
+            msg->setBuffer(key.c_str(), buffer);
+        }
+    }
+
+    *out = msg;
+
+    return OK;
+}
+
+}  // namespace android
+
diff --git a/media/jni/android_media_Streams.h b/media/jni/android_media_Streams.h
new file mode 100644
index 0000000..d174f9a
--- /dev/null
+++ b/media/jni/android_media_Streams.h
@@ -0,0 +1,116 @@
+/*
+ * Copyright 2019, 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_MEDIA_STREAMS_H_
+#define _ANDROID_MEDIA_STREAMS_H_
+
+#include "src/piex_types.h"
+#include "src/piex.h"
+
+#include <jni.h>
+#include <nativehelper/JNIHelp.h>
+#include <utils/KeyedVector.h>
+#include <utils/String8.h>
+#include <utils/StrongPointer.h>
+#include <SkStream.h>
+
+
+namespace android {
+
+class AssetStream : public piex::StreamInterface {
+private:
+    SkStream *mStream;
+    size_t mPosition;
+
+public:
+    explicit AssetStream(SkStream* stream);
+    ~AssetStream();
+
+    // Reads 'length' amount of bytes from 'offset' to 'data'. The 'data' buffer
+    // provided by the caller, guaranteed to be at least "length" bytes long.
+    // On 'kOk' the 'data' pointer contains 'length' valid bytes beginning at
+    // 'offset' bytes from the start of the stream.
+    // Returns 'kFail' if 'offset' + 'length' exceeds the stream and does not
+    // change the contents of 'data'.
+    piex::Error GetData(
+            const size_t offset, const size_t length, std::uint8_t* data) override;
+};
+
+class BufferedStream : public piex::StreamInterface {
+private:
+    SkStream *mStream;
+    // Growable memory stream
+    SkDynamicMemoryWStream mStreamBuffer;
+
+    // Minimum size to read on filling the buffer.
+    const size_t kMinSizeToRead = 8192;
+
+public:
+    explicit BufferedStream(SkStream* stream);
+    ~BufferedStream();
+
+    // Reads 'length' amount of bytes from 'offset' to 'data'. The 'data' buffer
+    // provided by the caller, guaranteed to be at least "length" bytes long.
+    // On 'kOk' the 'data' pointer contains 'length' valid bytes beginning at
+    // 'offset' bytes from the start of the stream.
+    // Returns 'kFail' if 'offset' + 'length' exceeds the stream and does not
+    // change the contents of 'data'.
+    piex::Error GetData(
+            const size_t offset, const size_t length, std::uint8_t* data) override;
+};
+
+class FileStream : public piex::StreamInterface {
+private:
+    FILE *mFile;
+    size_t mPosition;
+
+public:
+    explicit FileStream(const int fd);
+    explicit FileStream(const String8 filename);
+    ~FileStream();
+
+    // Reads 'length' amount of bytes from 'offset' to 'data'. The 'data' buffer
+    // provided by the caller, guaranteed to be at least "length" bytes long.
+    // On 'kOk' the 'data' pointer contains 'length' valid bytes beginning at
+    // 'offset' bytes from the start of the stream.
+    // Returns 'kFail' if 'offset' + 'length' exceeds the stream and does not
+    // change the contents of 'data'.
+    piex::Error GetData(
+            const size_t offset, const size_t length, std::uint8_t* data) override;
+    bool exists() const;
+};
+
+// Reads EXIF metadata from a given raw image via piex.
+// And returns true if the operation is successful; otherwise, false.
+bool GetExifFromRawImage(
+        piex::StreamInterface* stream, const String8& filename, piex::PreviewImageData& image_data);
+
+// Returns true if the conversion is successful; otherwise, false.
+bool ConvertKeyValueArraysToKeyedVector(
+        JNIEnv *env, jobjectArray keys, jobjectArray values,
+        KeyedVector<String8, String8>* vector);
+
+struct AMessage;
+status_t ConvertMessageToMap(
+        JNIEnv *env, const sp<AMessage> &msg, jobject *map);
+
+status_t ConvertKeyValueArraysToMessage(
+        JNIEnv *env, jobjectArray keys, jobjectArray values,
+        sp<AMessage> *msg);
+
+};  // namespace android
+
+#endif //  _ANDROID_MEDIA_STREAMS_H_
diff --git a/media/jni/android_media_Utils.cpp b/media/jni/android_media_Utils.cpp
index 458d847..8bdf534 100644
--- a/media/jni/android_media_Utils.cpp
+++ b/media/jni/android_media_Utils.cpp
@@ -21,543 +21,10 @@
 #include <utils/Log.h>
 #include "android_media_Utils.h"
 
-#include <media/stagefright/foundation/ADebug.h>
-#include <media/stagefright/foundation/ABuffer.h>
-#include <media/stagefright/foundation/AMessage.h>
-
-#include <nativehelper/ScopedLocalRef.h>
-
 #define ALIGN(x, mask) ( ((x) + (mask) - 1) & ~((mask) - 1) )
 
 namespace android {
 
-AssetStream::AssetStream(SkStream* stream)
-    : mStream(stream), mPosition(0) {
-}
-
-AssetStream::~AssetStream() {
-}
-
-piex::Error AssetStream::GetData(
-        const size_t offset, const size_t length, std::uint8_t* data) {
-    // Seek first.
-    if (mPosition != offset) {
-        if (!mStream->seek(offset)) {
-            return piex::Error::kFail;
-        }
-    }
-
-    // Read bytes.
-    size_t size = mStream->read((void*)data, length);
-    mPosition = offset + size;
-
-    return size == length ? piex::Error::kOk : piex::Error::kFail;
-}
-
-BufferedStream::BufferedStream(SkStream* stream)
-    : mStream(stream) {
-}
-
-BufferedStream::~BufferedStream() {
-}
-
-piex::Error BufferedStream::GetData(
-        const size_t offset, const size_t length, std::uint8_t* data) {
-    // Seek first.
-    if (offset + length > mStreamBuffer.bytesWritten()) {
-        size_t sizeToRead = offset + length - mStreamBuffer.bytesWritten();
-        if (sizeToRead <= kMinSizeToRead) {
-            sizeToRead = kMinSizeToRead;
-        }
-
-        void* tempBuffer = malloc(sizeToRead);
-        if (tempBuffer == NULL) {
-          return piex::Error::kFail;
-        }
-
-        size_t bytesRead = mStream->read(tempBuffer, sizeToRead);
-        if (bytesRead != sizeToRead) {
-            free(tempBuffer);
-            return piex::Error::kFail;
-        }
-        mStreamBuffer.write(tempBuffer, bytesRead);
-        free(tempBuffer);
-    }
-
-    // Read bytes.
-    if (mStreamBuffer.read((void*)data, offset, length)) {
-        return piex::Error::kOk;
-    } else {
-        return piex::Error::kFail;
-    }
-}
-
-FileStream::FileStream(const int fd)
-    : mPosition(0) {
-    mFile = fdopen(fd, "r");
-    if (mFile == NULL) {
-        return;
-    }
-}
-
-FileStream::FileStream(const String8 filename)
-    : mPosition(0) {
-    mFile = fopen(filename.string(), "r");
-    if (mFile == NULL) {
-        return;
-    }
-}
-
-FileStream::~FileStream() {
-    if (mFile != NULL) {
-        fclose(mFile);
-        mFile = NULL;
-    }
-}
-
-piex::Error FileStream::GetData(
-        const size_t offset, const size_t length, std::uint8_t* data) {
-    if (mFile == NULL) {
-        return piex::Error::kFail;
-    }
-
-    // Seek first.
-    if (mPosition != offset) {
-        fseek(mFile, offset, SEEK_SET);
-    }
-
-    // Read bytes.
-    size_t size = fread((void*)data, sizeof(std::uint8_t), length, mFile);
-    mPosition += size;
-
-    // Handle errors and verify the size.
-    if (ferror(mFile) || size != length) {
-        ALOGV("GetData read failed: (offset: %zu, length: %zu)", offset, length);
-        return piex::Error::kFail;
-    }
-    return piex::Error::kOk;
-}
-
-bool FileStream::exists() const {
-    return mFile != NULL;
-}
-
-bool GetExifFromRawImage(
-        piex::StreamInterface* stream, const String8& filename,
-        piex::PreviewImageData& image_data) {
-    // Reset the PreviewImageData to its default.
-    image_data = piex::PreviewImageData();
-
-    if (!piex::IsRaw(stream)) {
-        // Format not supported.
-        ALOGV("Format not supported: %s", filename.string());
-        return false;
-    }
-
-    piex::Error err = piex::GetPreviewImageData(stream, &image_data);
-
-    if (err != piex::Error::kOk) {
-        // The input data seems to be broken.
-        ALOGV("Raw image not detected: %s (piex error code: %d)", filename.string(), (int32_t)err);
-        return false;
-    }
-
-    return true;
-}
-
-bool ConvertKeyValueArraysToKeyedVector(
-        JNIEnv *env, jobjectArray keys, jobjectArray values,
-        KeyedVector<String8, String8>* keyedVector) {
-
-    int nKeyValuePairs = 0;
-    bool failed = false;
-    if (keys != NULL && values != NULL) {
-        nKeyValuePairs = env->GetArrayLength(keys);
-        failed = (nKeyValuePairs != env->GetArrayLength(values));
-    }
-
-    if (!failed) {
-        failed = ((keys != NULL && values == NULL) ||
-                  (keys == NULL && values != NULL));
-    }
-
-    if (failed) {
-        ALOGE("keys and values arrays have different length");
-        jniThrowException(env, "java/lang/IllegalArgumentException", NULL);
-        return false;
-    }
-
-    for (int i = 0; i < nKeyValuePairs; ++i) {
-        // No need to check on the ArrayIndexOutOfBoundsException, since
-        // it won't happen here.
-        jstring key = (jstring) env->GetObjectArrayElement(keys, i);
-        jstring value = (jstring) env->GetObjectArrayElement(values, i);
-
-        const char* keyStr = env->GetStringUTFChars(key, NULL);
-        if (!keyStr) {  // OutOfMemoryError
-            return false;
-        }
-
-        const char* valueStr = env->GetStringUTFChars(value, NULL);
-        if (!valueStr) {  // OutOfMemoryError
-            env->ReleaseStringUTFChars(key, keyStr);
-            return false;
-        }
-
-        keyedVector->add(String8(keyStr), String8(valueStr));
-
-        env->ReleaseStringUTFChars(key, keyStr);
-        env->ReleaseStringUTFChars(value, valueStr);
-        env->DeleteLocalRef(key);
-        env->DeleteLocalRef(value);
-    }
-    return true;
-}
-
-static jobject makeIntegerObject(JNIEnv *env, int32_t value) {
-    ScopedLocalRef<jclass> clazz(env, env->FindClass("java/lang/Integer"));
-    CHECK(clazz.get() != NULL);
-
-    jmethodID integerConstructID =
-        env->GetMethodID(clazz.get(), "<init>", "(I)V");
-    CHECK(integerConstructID != NULL);
-
-    return env->NewObject(clazz.get(), integerConstructID, value);
-}
-
-static jobject makeLongObject(JNIEnv *env, int64_t value) {
-    ScopedLocalRef<jclass> clazz(env, env->FindClass("java/lang/Long"));
-    CHECK(clazz.get() != NULL);
-
-    jmethodID longConstructID = env->GetMethodID(clazz.get(), "<init>", "(J)V");
-    CHECK(longConstructID != NULL);
-
-    return env->NewObject(clazz.get(), longConstructID, value);
-}
-
-static jobject makeFloatObject(JNIEnv *env, float value) {
-    ScopedLocalRef<jclass> clazz(env, env->FindClass("java/lang/Float"));
-    CHECK(clazz.get() != NULL);
-
-    jmethodID floatConstructID =
-        env->GetMethodID(clazz.get(), "<init>", "(F)V");
-    CHECK(floatConstructID != NULL);
-
-    return env->NewObject(clazz.get(), floatConstructID, value);
-}
-
-static jobject makeByteBufferObject(
-        JNIEnv *env, const void *data, size_t size) {
-    jbyteArray byteArrayObj = env->NewByteArray(size);
-    env->SetByteArrayRegion(byteArrayObj, 0, size, (const jbyte *)data);
-
-    ScopedLocalRef<jclass> clazz(env, env->FindClass("java/nio/ByteBuffer"));
-    CHECK(clazz.get() != NULL);
-
-    jmethodID byteBufWrapID =
-        env->GetStaticMethodID(
-                clazz.get(), "wrap", "([B)Ljava/nio/ByteBuffer;");
-    CHECK(byteBufWrapID != NULL);
-
-    jobject byteBufObj = env->CallStaticObjectMethod(
-            clazz.get(), byteBufWrapID, byteArrayObj);
-
-    env->DeleteLocalRef(byteArrayObj); byteArrayObj = NULL;
-
-    return byteBufObj;
-}
-
-static void SetMapInt32(
-        JNIEnv *env, jobject hashMapObj, jmethodID hashMapPutID,
-        const char *key, int32_t value) {
-    jstring keyObj = env->NewStringUTF(key);
-    jobject valueObj = makeIntegerObject(env, value);
-
-    env->CallObjectMethod(hashMapObj, hashMapPutID, keyObj, valueObj);
-
-    env->DeleteLocalRef(valueObj); valueObj = NULL;
-    env->DeleteLocalRef(keyObj); keyObj = NULL;
-}
-
-status_t ConvertMessageToMap(
-        JNIEnv *env, const sp<AMessage> &msg, jobject *map) {
-    ScopedLocalRef<jclass> hashMapClazz(
-            env, env->FindClass("java/util/HashMap"));
-
-    if (hashMapClazz.get() == NULL) {
-        return -EINVAL;
-    }
-
-    jmethodID hashMapConstructID =
-        env->GetMethodID(hashMapClazz.get(), "<init>", "()V");
-
-    if (hashMapConstructID == NULL) {
-        return -EINVAL;
-    }
-
-    jmethodID hashMapPutID =
-        env->GetMethodID(
-                hashMapClazz.get(),
-                "put",
-                "(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;");
-
-    if (hashMapPutID == NULL) {
-        return -EINVAL;
-    }
-
-    jobject hashMap = env->NewObject(hashMapClazz.get(), hashMapConstructID);
-
-    for (size_t i = 0; i < msg->countEntries(); ++i) {
-        AMessage::Type valueType;
-        const char *key = msg->getEntryNameAt(i, &valueType);
-
-        if (!strncmp(key, "android._", 9)) {
-            // don't expose private keys (starting with android._)
-            continue;
-        }
-
-        jobject valueObj = NULL;
-
-        switch (valueType) {
-            case AMessage::kTypeInt32:
-            {
-                int32_t val;
-                CHECK(msg->findInt32(key, &val));
-
-                valueObj = makeIntegerObject(env, val);
-                break;
-            }
-
-            case AMessage::kTypeInt64:
-            {
-                int64_t val;
-                CHECK(msg->findInt64(key, &val));
-
-                valueObj = makeLongObject(env, val);
-                break;
-            }
-
-            case AMessage::kTypeFloat:
-            {
-                float val;
-                CHECK(msg->findFloat(key, &val));
-
-                valueObj = makeFloatObject(env, val);
-                break;
-            }
-
-            case AMessage::kTypeString:
-            {
-                AString val;
-                CHECK(msg->findString(key, &val));
-
-                valueObj = env->NewStringUTF(val.c_str());
-                break;
-            }
-
-            case AMessage::kTypeBuffer:
-            {
-                sp<ABuffer> buffer;
-                CHECK(msg->findBuffer(key, &buffer));
-
-                valueObj = makeByteBufferObject(
-                        env, buffer->data(), buffer->size());
-                break;
-            }
-
-            case AMessage::kTypeRect:
-            {
-                int32_t left, top, right, bottom;
-                CHECK(msg->findRect(key, &left, &top, &right, &bottom));
-
-                SetMapInt32(
-                        env,
-                        hashMap,
-                        hashMapPutID,
-                        AStringPrintf("%s-left", key).c_str(),
-                        left);
-
-                SetMapInt32(
-                        env,
-                        hashMap,
-                        hashMapPutID,
-                        AStringPrintf("%s-top", key).c_str(),
-                        top);
-
-                SetMapInt32(
-                        env,
-                        hashMap,
-                        hashMapPutID,
-                        AStringPrintf("%s-right", key).c_str(),
-                        right);
-
-                SetMapInt32(
-                        env,
-                        hashMap,
-                        hashMapPutID,
-                        AStringPrintf("%s-bottom", key).c_str(),
-                        bottom);
-                break;
-            }
-
-            default:
-                break;
-        }
-
-        if (valueObj != NULL) {
-            jstring keyObj = env->NewStringUTF(key);
-
-            env->CallObjectMethod(hashMap, hashMapPutID, keyObj, valueObj);
-
-            env->DeleteLocalRef(keyObj); keyObj = NULL;
-            env->DeleteLocalRef(valueObj); valueObj = NULL;
-        }
-    }
-
-    *map = hashMap;
-
-    return OK;
-}
-
-status_t ConvertKeyValueArraysToMessage(
-        JNIEnv *env, jobjectArray keys, jobjectArray values,
-        sp<AMessage> *out) {
-    ScopedLocalRef<jclass> stringClass(env, env->FindClass("java/lang/String"));
-    CHECK(stringClass.get() != NULL);
-    ScopedLocalRef<jclass> integerClass(env, env->FindClass("java/lang/Integer"));
-    CHECK(integerClass.get() != NULL);
-    ScopedLocalRef<jclass> longClass(env, env->FindClass("java/lang/Long"));
-    CHECK(longClass.get() != NULL);
-    ScopedLocalRef<jclass> floatClass(env, env->FindClass("java/lang/Float"));
-    CHECK(floatClass.get() != NULL);
-    ScopedLocalRef<jclass> byteBufClass(env, env->FindClass("java/nio/ByteBuffer"));
-    CHECK(byteBufClass.get() != NULL);
-
-    sp<AMessage> msg = new AMessage;
-
-    jsize numEntries = 0;
-
-    if (keys != NULL) {
-        if (values == NULL) {
-            return -EINVAL;
-        }
-
-        numEntries = env->GetArrayLength(keys);
-
-        if (numEntries != env->GetArrayLength(values)) {
-            return -EINVAL;
-        }
-    } else if (values != NULL) {
-        return -EINVAL;
-    }
-
-    for (jsize i = 0; i < numEntries; ++i) {
-        jobject keyObj = env->GetObjectArrayElement(keys, i);
-
-        if (!env->IsInstanceOf(keyObj, stringClass.get())) {
-            return -EINVAL;
-        }
-
-        const char *tmp = env->GetStringUTFChars((jstring)keyObj, NULL);
-
-        if (tmp == NULL) {
-            return -ENOMEM;
-        }
-
-        AString key = tmp;
-
-        env->ReleaseStringUTFChars((jstring)keyObj, tmp);
-        tmp = NULL;
-
-        if (key.startsWith("android._")) {
-            // don't propagate private keys (starting with android._)
-            continue;
-        }
-
-        jobject valueObj = env->GetObjectArrayElement(values, i);
-
-        if (env->IsInstanceOf(valueObj, stringClass.get())) {
-            const char *value = env->GetStringUTFChars((jstring)valueObj, NULL);
-
-            if (value == NULL) {
-                return -ENOMEM;
-            }
-
-            msg->setString(key.c_str(), value);
-
-            env->ReleaseStringUTFChars((jstring)valueObj, value);
-            value = NULL;
-        } else if (env->IsInstanceOf(valueObj, integerClass.get())) {
-            jmethodID intValueID =
-                env->GetMethodID(integerClass.get(), "intValue", "()I");
-            CHECK(intValueID != NULL);
-
-            jint value = env->CallIntMethod(valueObj, intValueID);
-
-            msg->setInt32(key.c_str(), value);
-        } else if (env->IsInstanceOf(valueObj, longClass.get())) {
-            jmethodID longValueID =
-                env->GetMethodID(longClass.get(), "longValue", "()J");
-            CHECK(longValueID != NULL);
-
-            jlong value = env->CallLongMethod(valueObj, longValueID);
-
-            msg->setInt64(key.c_str(), value);
-        } else if (env->IsInstanceOf(valueObj, floatClass.get())) {
-            jmethodID floatValueID =
-                env->GetMethodID(floatClass.get(), "floatValue", "()F");
-            CHECK(floatValueID != NULL);
-
-            jfloat value = env->CallFloatMethod(valueObj, floatValueID);
-
-            msg->setFloat(key.c_str(), value);
-        } else if (env->IsInstanceOf(valueObj, byteBufClass.get())) {
-            jmethodID positionID =
-                env->GetMethodID(byteBufClass.get(), "position", "()I");
-            CHECK(positionID != NULL);
-
-            jmethodID limitID =
-                env->GetMethodID(byteBufClass.get(), "limit", "()I");
-            CHECK(limitID != NULL);
-
-            jint position = env->CallIntMethod(valueObj, positionID);
-            jint limit = env->CallIntMethod(valueObj, limitID);
-
-            sp<ABuffer> buffer = new ABuffer(limit - position);
-
-            void *data = env->GetDirectBufferAddress(valueObj);
-
-            if (data != NULL) {
-                memcpy(buffer->data(),
-                       (const uint8_t *)data + position,
-                       buffer->size());
-            } else {
-                jmethodID arrayID =
-                    env->GetMethodID(byteBufClass.get(), "array", "()[B");
-                CHECK(arrayID != NULL);
-
-                jbyteArray byteArray =
-                    (jbyteArray)env->CallObjectMethod(valueObj, arrayID);
-                CHECK(byteArray != NULL);
-
-                env->GetByteArrayRegion(
-                        byteArray,
-                        position,
-                        buffer->size(),
-                        (jbyte *)buffer->data());
-
-                env->DeleteLocalRef(byteArray); byteArray = NULL;
-            }
-
-            msg->setBuffer(key.c_str(), buffer);
-        }
-    }
-
-    *out = msg;
-
-    return OK;
-}
-
 // -----------Utility functions used by ImageReader/Writer JNI-----------------
 
 enum {
diff --git a/media/jni/android_media_Utils.h b/media/jni/android_media_Utils.h
index 821c6b2..24f1610 100644
--- a/media/jni/android_media_Utils.h
+++ b/media/jni/android_media_Utils.h
@@ -17,100 +17,10 @@
 #ifndef _ANDROID_MEDIA_UTILS_H_
 #define _ANDROID_MEDIA_UTILS_H_
 
-#include "src/piex_types.h"
-#include "src/piex.h"
-
-#include <android_runtime/AndroidRuntime.h>
 #include <gui/CpuConsumer.h>
-#include <jni.h>
-#include <nativehelper/JNIHelp.h>
-#include <utils/KeyedVector.h>
-#include <utils/String8.h>
-#include <SkStream.h>
 
 namespace android {
 
-class AssetStream : public piex::StreamInterface {
-private:
-    SkStream *mStream;
-    size_t mPosition;
-
-public:
-    explicit AssetStream(SkStream* stream);
-    ~AssetStream();
-
-    // Reads 'length' amount of bytes from 'offset' to 'data'. The 'data' buffer
-    // provided by the caller, guaranteed to be at least "length" bytes long.
-    // On 'kOk' the 'data' pointer contains 'length' valid bytes beginning at
-    // 'offset' bytes from the start of the stream.
-    // Returns 'kFail' if 'offset' + 'length' exceeds the stream and does not
-    // change the contents of 'data'.
-    piex::Error GetData(
-            const size_t offset, const size_t length, std::uint8_t* data) override;
-};
-
-class BufferedStream : public piex::StreamInterface {
-private:
-    SkStream *mStream;
-    // Growable memory stream
-    SkDynamicMemoryWStream mStreamBuffer;
-
-    // Minimum size to read on filling the buffer.
-    const size_t kMinSizeToRead = 8192;
-
-public:
-    explicit BufferedStream(SkStream* stream);
-    ~BufferedStream();
-
-    // Reads 'length' amount of bytes from 'offset' to 'data'. The 'data' buffer
-    // provided by the caller, guaranteed to be at least "length" bytes long.
-    // On 'kOk' the 'data' pointer contains 'length' valid bytes beginning at
-    // 'offset' bytes from the start of the stream.
-    // Returns 'kFail' if 'offset' + 'length' exceeds the stream and does not
-    // change the contents of 'data'.
-    piex::Error GetData(
-            const size_t offset, const size_t length, std::uint8_t* data) override;
-};
-
-class FileStream : public piex::StreamInterface {
-private:
-    FILE *mFile;
-    size_t mPosition;
-
-public:
-    explicit FileStream(const int fd);
-    explicit FileStream(const String8 filename);
-    ~FileStream();
-
-    // Reads 'length' amount of bytes from 'offset' to 'data'. The 'data' buffer
-    // provided by the caller, guaranteed to be at least "length" bytes long.
-    // On 'kOk' the 'data' pointer contains 'length' valid bytes beginning at
-    // 'offset' bytes from the start of the stream.
-    // Returns 'kFail' if 'offset' + 'length' exceeds the stream and does not
-    // change the contents of 'data'.
-    piex::Error GetData(
-            const size_t offset, const size_t length, std::uint8_t* data) override;
-    bool exists() const;
-};
-
-// Reads EXIF metadata from a given raw image via piex.
-// And returns true if the operation is successful; otherwise, false.
-bool GetExifFromRawImage(
-        piex::StreamInterface* stream, const String8& filename, piex::PreviewImageData& image_data);
-
-// Returns true if the conversion is successful; otherwise, false.
-bool ConvertKeyValueArraysToKeyedVector(
-        JNIEnv *env, jobjectArray keys, jobjectArray values,
-        KeyedVector<String8, String8>* vector);
-
-struct AMessage;
-status_t ConvertMessageToMap(
-        JNIEnv *env, const sp<AMessage> &msg, jobject *map);
-
-status_t ConvertKeyValueArraysToMessage(
-        JNIEnv *env, jobjectArray keys, jobjectArray values,
-        sp<AMessage> *msg);
-
 // -----------Utility functions used by ImageReader/Writer JNI-----------------
 
 typedef CpuConsumer::LockedBuffer LockedImage;
diff --git a/media/jni/android_mtp_MtpDatabase.cpp b/media/jni/android_mtp_MtpDatabase.cpp
index 213c3d9..a6c5fc8 100644
--- a/media/jni/android_mtp_MtpDatabase.cpp
+++ b/media/jni/android_mtp_MtpDatabase.cpp
@@ -18,7 +18,7 @@
 #include "utils/Log.h"
 #include "utils/String8.h"
 
-#include "android_media_Utils.h"
+#include "android_media_Streams.h"
 #include "mtp.h"
 #include "IMtpDatabase.h"
 #include "MtpDataPacket.h"
diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java
index cc3e6fb..ac71ae5 100644
--- a/services/core/java/com/android/server/ConnectivityService.java
+++ b/services/core/java/com/android/server/ConnectivityService.java
@@ -1573,8 +1573,7 @@
     public boolean isActiveNetworkMetered() {
         enforceAccessPermission();
 
-        final int uid = Binder.getCallingUid();
-        final NetworkCapabilities caps = getUnfilteredActiveNetworkState(uid).networkCapabilities;
+        final NetworkCapabilities caps = getNetworkCapabilities(getActiveNetwork());
         if (caps != null) {
             return !caps.hasCapability(NetworkCapabilities.NET_CAPABILITY_NOT_METERED);
         } else {
diff --git a/tests/net/java/com/android/server/ConnectivityServiceTest.java b/tests/net/java/com/android/server/ConnectivityServiceTest.java
index b460c90..c25ad5f 100644
--- a/tests/net/java/com/android/server/ConnectivityServiceTest.java
+++ b/tests/net/java/com/android/server/ConnectivityServiceTest.java
@@ -909,11 +909,19 @@
             return mConnected;  // Similar trickery
         }
 
-        public void connect() {
+        private void connect(boolean isAlwaysMetered) {
             mNetworkCapabilities.set(mMockNetworkAgent.getNetworkCapabilities());
             mConnected = true;
             mConfig = new VpnConfig();
-            mConfig.isMetered = false;
+            mConfig.isMetered = isAlwaysMetered;
+        }
+
+        public void connectAsAlwaysMetered() {
+            connect(true /* isAlwaysMetered */);
+        }
+
+        public void connect() {
+            connect(false /* isAlwaysMetered */);
         }
 
         @Override
@@ -5015,6 +5023,202 @@
     }
 
     @Test
+    public void testIsActiveNetworkMeteredOverWifi() {
+        // Returns true by default when no network is available.
+        assertTrue(mCm.isActiveNetworkMetered());
+        mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI);
+        mWiFiNetworkAgent.addCapability(NET_CAPABILITY_NOT_METERED);
+        mWiFiNetworkAgent.connect(true);
+        waitForIdle();
+
+        assertFalse(mCm.isActiveNetworkMetered());
+    }
+
+    @Test
+    public void testIsActiveNetworkMeteredOverCell() {
+        // Returns true by default when no network is available.
+        assertTrue(mCm.isActiveNetworkMetered());
+        mCellNetworkAgent = new MockNetworkAgent(TRANSPORT_CELLULAR);
+        mCellNetworkAgent.removeCapability(NET_CAPABILITY_NOT_METERED);
+        mCellNetworkAgent.connect(true);
+        waitForIdle();
+
+        assertTrue(mCm.isActiveNetworkMetered());
+    }
+
+    @Test
+    public void testIsActiveNetworkMeteredOverVpnTrackingPlatformDefault() {
+        // Returns true by default when no network is available.
+        assertTrue(mCm.isActiveNetworkMetered());
+        mCellNetworkAgent = new MockNetworkAgent(TRANSPORT_CELLULAR);
+        mCellNetworkAgent.removeCapability(NET_CAPABILITY_NOT_METERED);
+        mCellNetworkAgent.connect(true);
+        waitForIdle();
+        assertTrue(mCm.isActiveNetworkMetered());
+
+        // Connect VPN network. By default it is using current default network (Cell).
+        MockNetworkAgent vpnNetworkAgent = new MockNetworkAgent(TRANSPORT_VPN);
+        final ArraySet<UidRange> ranges = new ArraySet<>();
+        final int uid = Process.myUid();
+        ranges.add(new UidRange(uid, uid));
+        mMockVpn.setNetworkAgent(vpnNetworkAgent);
+        mMockVpn.setUids(ranges);
+        vpnNetworkAgent.connect(true);
+        mMockVpn.connect();
+        waitForIdle();
+        // Ensure VPN is now the active network.
+        assertEquals(vpnNetworkAgent.getNetwork(), mCm.getActiveNetwork());
+
+        // Expect VPN to be metered.
+        assertTrue(mCm.isActiveNetworkMetered());
+
+        // Connect WiFi.
+        mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI);
+        mWiFiNetworkAgent.addCapability(NET_CAPABILITY_NOT_METERED);
+        mWiFiNetworkAgent.connect(true);
+        waitForIdle();
+        // VPN should still be the active network.
+        assertEquals(vpnNetworkAgent.getNetwork(), mCm.getActiveNetwork());
+
+        // Expect VPN to be unmetered as it should now be using WiFi (new default).
+        assertFalse(mCm.isActiveNetworkMetered());
+
+        // Disconnecting Cell should not affect VPN's meteredness.
+        mCellNetworkAgent.disconnect();
+        waitForIdle();
+
+        assertFalse(mCm.isActiveNetworkMetered());
+
+        // Disconnect WiFi; Now there is no platform default network.
+        mWiFiNetworkAgent.disconnect();
+        waitForIdle();
+
+        // VPN without any underlying networks is treated as metered.
+        assertTrue(mCm.isActiveNetworkMetered());
+
+        vpnNetworkAgent.disconnect();
+        mMockVpn.disconnect();
+    }
+
+   @Test
+   public void testIsActiveNetworkMeteredOverVpnSpecifyingUnderlyingNetworks() {
+        // Returns true by default when no network is available.
+        assertTrue(mCm.isActiveNetworkMetered());
+        mCellNetworkAgent = new MockNetworkAgent(TRANSPORT_CELLULAR);
+        mCellNetworkAgent.removeCapability(NET_CAPABILITY_NOT_METERED);
+        mCellNetworkAgent.connect(true);
+        waitForIdle();
+        assertTrue(mCm.isActiveNetworkMetered());
+
+        mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI);
+        mWiFiNetworkAgent.addCapability(NET_CAPABILITY_NOT_METERED);
+        mWiFiNetworkAgent.connect(true);
+        waitForIdle();
+        assertFalse(mCm.isActiveNetworkMetered());
+
+        // Connect VPN network.
+        MockNetworkAgent vpnNetworkAgent = new MockNetworkAgent(TRANSPORT_VPN);
+        final ArraySet<UidRange> ranges = new ArraySet<>();
+        final int uid = Process.myUid();
+        ranges.add(new UidRange(uid, uid));
+        mMockVpn.setNetworkAgent(vpnNetworkAgent);
+        mMockVpn.setUids(ranges);
+        vpnNetworkAgent.connect(true);
+        mMockVpn.connect();
+        waitForIdle();
+        // Ensure VPN is now the active network.
+        assertEquals(vpnNetworkAgent.getNetwork(), mCm.getActiveNetwork());
+        // VPN is using Cell
+        mService.setUnderlyingNetworksForVpn(
+                new Network[] { mCellNetworkAgent.getNetwork() });
+        waitForIdle();
+
+        // Expect VPN to be metered.
+        assertTrue(mCm.isActiveNetworkMetered());
+
+        // VPN is now using WiFi
+        mService.setUnderlyingNetworksForVpn(
+                new Network[] { mWiFiNetworkAgent.getNetwork() });
+        waitForIdle();
+
+        // Expect VPN to be unmetered
+        assertFalse(mCm.isActiveNetworkMetered());
+
+        // VPN is using Cell | WiFi.
+        mService.setUnderlyingNetworksForVpn(
+                new Network[] { mCellNetworkAgent.getNetwork(), mWiFiNetworkAgent.getNetwork() });
+        waitForIdle();
+
+        // Expect VPN to be metered.
+        assertTrue(mCm.isActiveNetworkMetered());
+
+        // VPN is using WiFi | Cell.
+        mService.setUnderlyingNetworksForVpn(
+                new Network[] { mWiFiNetworkAgent.getNetwork(), mCellNetworkAgent.getNetwork() });
+        waitForIdle();
+
+        // Order should not matter and VPN should still be metered.
+        assertTrue(mCm.isActiveNetworkMetered());
+
+        // VPN is not using any underlying networks.
+        mService.setUnderlyingNetworksForVpn(new Network[0]);
+        waitForIdle();
+
+        // VPN without underlying networks is treated as metered.
+        assertTrue(mCm.isActiveNetworkMetered());
+
+        vpnNetworkAgent.disconnect();
+        mMockVpn.disconnect();
+    }
+
+    @Test
+    public void testIsActiveNetworkMeteredOverAlwaysMeteredVpn() {
+        // Returns true by default when no network is available.
+        assertTrue(mCm.isActiveNetworkMetered());
+        mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI);
+        mWiFiNetworkAgent.addCapability(NET_CAPABILITY_NOT_METERED);
+        mWiFiNetworkAgent.connect(true);
+        waitForIdle();
+        assertFalse(mCm.isActiveNetworkMetered());
+
+        // Connect VPN network.
+        MockNetworkAgent vpnNetworkAgent = new MockNetworkAgent(TRANSPORT_VPN);
+        final ArraySet<UidRange> ranges = new ArraySet<>();
+        final int uid = Process.myUid();
+        ranges.add(new UidRange(uid, uid));
+        mMockVpn.setNetworkAgent(vpnNetworkAgent);
+        mMockVpn.setUids(ranges);
+        vpnNetworkAgent.connect(true);
+        mMockVpn.connectAsAlwaysMetered();
+        waitForIdle();
+        assertEquals(vpnNetworkAgent.getNetwork(), mCm.getActiveNetwork());
+
+        // VPN is tracking current platform default (WiFi).
+        mService.setUnderlyingNetworksForVpn(null);
+        waitForIdle();
+
+        // Despite VPN using WiFi (which is unmetered), VPN itself is marked as always metered.
+        assertTrue(mCm.isActiveNetworkMetered());
+
+        // VPN explicitly declares WiFi as its underlying network.
+        mService.setUnderlyingNetworksForVpn(
+                new Network[] { mWiFiNetworkAgent.getNetwork() });
+        waitForIdle();
+
+        // Doesn't really matter whether VPN declares its underlying networks explicitly.
+        assertTrue(mCm.isActiveNetworkMetered());
+
+        // With WiFi lost, VPN is basically without any underlying networks. And in that case it is
+        // anyways suppose to be metered.
+        mWiFiNetworkAgent.disconnect();
+        waitForIdle();
+
+        assertTrue(mCm.isActiveNetworkMetered());
+
+        vpnNetworkAgent.disconnect();
+    }
+
+    @Test
     public void testNetworkBlockedStatus() {
         final TestNetworkCallback cellNetworkCallback = new TestNetworkCallback();
         final NetworkRequest cellRequest = new NetworkRequest.Builder()