Merge "Clear up bit mask usage in audio policy."
diff --git a/apex/ld.config.txt b/apex/ld.config.txt
index 8f60bb2..5db59e5 100644
--- a/apex/ld.config.txt
+++ b/apex/ld.config.txt
@@ -95,7 +95,7 @@
 
 namespace.sphal.search.paths += /odm/${LIB}/vndk-sp
 namespace.sphal.search.paths += /vendor/${LIB}/vndk-sp
-namespace.sphal.search.paths += /system/${LIB}/vndk-sp${VNDK_VER}
+namespace.sphal.search.paths += /apex/com.android.vndk.v${VNDK_VER}/${LIB}
 
 namespace.sphal.permitted.paths += /odm/${LIB}/hw
 namespace.sphal.permitted.paths += /odm/${LIB}/egl
@@ -104,14 +104,13 @@
 namespace.sphal.permitted.paths += /system/vendor/${LIB}/hw
 namespace.sphal.permitted.paths += /system/vendor/${LIB}/egl
 # This is exceptionally required since android.hidl.memory@1.0-impl.so is here
-namespace.sphal.permitted.paths += /system/${LIB}/vndk-sp${VNDK_VER}/hw
+namespace.sphal.permitted.paths += /apex/com.android.vndk.v${VNDK_VER}/${LIB}/hw
 
 namespace.sphal.asan.search.paths += /data/asan/odm/${LIB}/vndk-sp
 namespace.sphal.asan.search.paths +=           /odm/${LIB}/vndk-sp
 namespace.sphal.asan.search.paths += /data/asan/vendor/${LIB}/vndk-sp
 namespace.sphal.asan.search.paths +=           /vendor/${LIB}/vndk-sp
-namespace.sphal.asan.search.paths += /data/asan/system/${LIB}/vndk-sp${VNDK_VER}
-namespace.sphal.asan.search.paths +=           /system/${LIB}/vndk-sp${VNDK_VER}
+namespace.sphal.asan.search.paths += /apex/com.android.vndk.v${VNDK_VER}/${LIB}
 
 namespace.sphal.asan.permitted.paths += /data/asan/odm/${LIB}/hw
 namespace.sphal.asan.permitted.paths +=           /odm/${LIB}/hw
@@ -122,8 +121,7 @@
 namespace.sphal.asan.permitted.paths += /data/asan/vendor/${LIB}/egl
 namespace.sphal.asan.permitted.paths +=           /vendor/${LIB}/egl
 
-namespace.sphal.asan.permitted.paths += /data/asan/system/${LIB}/vndk-sp${VNDK_VER}/hw
-namespace.sphal.asan.permitted.paths +=           /system/${LIB}/vndk-sp${VNDK_VER}/hw
+namespace.sphal.asan.permitted.paths += /apex/com.android.vndk.v${VNDK_VER}/${LIB}/hw
 
 # Once in this namespace, access to libraries in /system/lib is restricted. Only
 # libs listed here can be used.
diff --git a/media/codec2/core/include/C2Config.h b/media/codec2/core/include/C2Config.h
index 3820f90..f9eb2fa 100644
--- a/media/codec2/core/include/C2Config.h
+++ b/media/codec2/core/include/C2Config.h
@@ -243,6 +243,9 @@
     kParamIndexTimestampGapAdjustment, // input-surface, struct
 
     kParamIndexSurfaceAllocator, // u32
+
+    // low latency mode for decoders
+    kParamIndexLowLatencyMode, // bool
 };
 
 }
@@ -521,6 +524,7 @@
     PROFILE_DV_HE_07 = _C2_PL_DV_BASE + 7,      ///< Dolby Vision dvhe.07 profile
     PROFILE_DV_HE_08 = _C2_PL_DV_BASE + 8,      ///< Dolby Vision dvhe.08 profile
     PROFILE_DV_AV_09 = _C2_PL_DV_BASE + 9,      ///< Dolby Vision dvav.09 profile
+    PROFILE_DV_AV1_10 = _C2_PL_DV_BASE + 10,    ///< Dolby Vision dav1.10 profile
 
     // AV1 profiles
     PROFILE_AV1_0 = _C2_PL_AV1_BASE,            ///< AV1 Profile 0 (4:2:0, 8 to 10 bit)
@@ -804,6 +808,15 @@
 constexpr char C2_PARAMKEY_PIPELINE_DELAY[] = "algo.delay";
 
 /**
+ * Enable/disable low latency decoding mode.
+ * If true, low latency decoding mode is enabled, and the decoder doesn't hold input and output
+ * data more than required by the codec standards.
+ */
+typedef C2GlobalParam<C2Tuning, C2EasyBoolValue, kParamIndexLowLatencyMode>
+        C2GlobalLowLatencyModeTuning;
+constexpr char C2_PARAMKEY_LOW_LATENCY_MODE[] = "algo.low-latency";
+
+/**
  * Reference characteristics.
  *
  * The component may hold onto input and output buffers even after completing the corresponding
diff --git a/media/codec2/sfplugin/CCodecConfig.cpp b/media/codec2/sfplugin/CCodecConfig.cpp
index 5adcd94..ee3cdf6 100644
--- a/media/codec2/sfplugin/CCodecConfig.cpp
+++ b/media/codec2/sfplugin/CCodecConfig.cpp
@@ -823,6 +823,14 @@
 
     add(ConfigMapper(C2_PARAMKEY_INPUT_TIME_STRETCH, C2_PARAMKEY_INPUT_TIME_STRETCH, "value"));
 
+    add(ConfigMapper(KEY_LOW_LATENCY, C2_PARAMKEY_LOW_LATENCY_MODE, "value")
+        .limitTo(D::DECODER & (D::CONFIG | D::PARAM))
+        .withMapper([](C2Value v) -> C2Value {
+            int32_t value = 0;
+            (void)v.get(&value);
+            return value == 0 ? C2_FALSE : C2_TRUE;
+        }));
+
     /* still to do
     constexpr char KEY_PUSH_BLANK_BUFFERS_ON_STOP[] = "push-blank-buffers-on-shutdown";
 
diff --git a/media/codec2/sfplugin/utils/Codec2Mapper.cpp b/media/codec2/sfplugin/utils/Codec2Mapper.cpp
index ef6af48..2f3d688 100644
--- a/media/codec2/sfplugin/utils/Codec2Mapper.cpp
+++ b/media/codec2/sfplugin/utils/Codec2Mapper.cpp
@@ -190,6 +190,7 @@
     { C2Config::PROFILE_DV_HE_07, DolbyVisionProfileDvheDtb },
     { C2Config::PROFILE_DV_HE_08, DolbyVisionProfileDvheSt },
     { C2Config::PROFILE_DV_AV_09, DolbyVisionProfileDvavSe },
+    { C2Config::PROFILE_DV_AV1_10, DolbyVisionProfileDvav110 },
 };
 
 ALookup<C2Config::level_t, int32_t> sH263Levels = {
diff --git a/media/codec2/vndk/C2AllocatorIon.cpp b/media/codec2/vndk/C2AllocatorIon.cpp
index 752bc46..0470a31 100644
--- a/media/codec2/vndk/C2AllocatorIon.cpp
+++ b/media/codec2/vndk/C2AllocatorIon.cpp
@@ -600,7 +600,7 @@
     }
 
     std::shared_ptr<C2AllocationIon> alloc
-        = std::make_shared<C2AllocationIon>(dup(mIonFd), capacity, align, heapMask, flags, mTraits->id);
+        = std::make_shared<C2AllocationIon>(dup(mIonFd), capacity, align, heapMask, flags, getId());
     ret = alloc->status();
     if (ret == C2_OK) {
         *allocation = alloc;
@@ -622,7 +622,7 @@
     // TODO: get capacity and validate it
     const C2HandleIon *h = static_cast<const C2HandleIon*>(handle);
     std::shared_ptr<C2AllocationIon> alloc
-        = std::make_shared<C2AllocationIon>(dup(mIonFd), h->size(), h->bufferFd(), mTraits->id);
+        = std::make_shared<C2AllocationIon>(dup(mIonFd), h->size(), h->bufferFd(), getId());
     c2_status_t ret = alloc->status();
     if (ret == C2_OK) {
         *allocation = alloc;
diff --git a/media/extractors/mp4/MPEG4Extractor.cpp b/media/extractors/mp4/MPEG4Extractor.cpp
index b91d16f..81e1b8c 100755
--- a/media/extractors/mp4/MPEG4Extractor.cpp
+++ b/media/extractors/mp4/MPEG4Extractor.cpp
@@ -133,6 +133,7 @@
 
     bool mIsAVC;
     bool mIsHEVC;
+    bool mIsDolbyVision;
     bool mIsAC4;
     bool mIsPcm;
     size_t mNALLengthSize;
@@ -337,6 +338,14 @@
         case FOURCC("hvc1"):
         case FOURCC("hev1"):
             return MEDIA_MIMETYPE_VIDEO_HEVC;
+
+        case FOURCC("dvav"):
+        case FOURCC("dva1"):
+        case FOURCC("dvhe"):
+        case FOURCC("dvh1"):
+        case FOURCC("dav1"):
+            return MEDIA_MIMETYPE_VIDEO_DOLBY_VISION;
+
         case FOURCC("ac-4"):
             return MEDIA_MIMETYPE_AUDIO_AC4;
         case FOURCC("Opus"):
@@ -1062,6 +1071,62 @@
                     mLastTrack->mTx3gBuffer = NULL;
                 }
 
+                const char *mime;
+                AMediaFormat_getString(mLastTrack->meta, AMEDIAFORMAT_KEY_MIME, &mime);
+
+                if (!strcasecmp(mime, MEDIA_MIMETYPE_VIDEO_DOLBY_VISION)) {
+                    void *data;
+                    size_t size;
+
+                    if (AMediaFormat_getBuffer(mLastTrack->meta, AMEDIAFORMAT_KEY_CSD_2, &data, &size)) {
+                        const uint8_t *ptr = (const uint8_t *)data;
+                        const uint8_t profile = ptr[2] >> 1;
+                        const uint8_t bl_compatibility_id = (ptr[4]) >> 4;
+
+                        if (4 == profile || 7 == profile ||
+                                (profile >= 8 && profile < 11 && bl_compatibility_id)) {
+                            // we need a backward compatible track
+                            ALOGV("Adding new backward compatible track");
+                            Track *track_b = new Track;
+
+                            track_b->timescale = mLastTrack->timescale;
+                            track_b->sampleTable = mLastTrack->sampleTable;
+                            track_b->includes_expensive_metadata = mLastTrack->includes_expensive_metadata;
+                            track_b->skipTrack = mLastTrack->skipTrack;
+                            track_b->has_elst = mLastTrack->has_elst;
+                            track_b->elst_media_time = mLastTrack->elst_media_time;
+                            track_b->elst_segment_duration = mLastTrack->elst_segment_duration;
+                            track_b->elstShiftStartTicks = mLastTrack->elstShiftStartTicks;
+                            track_b->subsample_encryption = mLastTrack->subsample_encryption;
+
+                            track_b->mTx3gBuffer = mLastTrack->mTx3gBuffer;
+                            track_b->mTx3gSize = mLastTrack->mTx3gSize;
+                            track_b->mTx3gFilled = mLastTrack->mTx3gFilled;
+
+                            track_b->meta = AMediaFormat_new();
+                            AMediaFormat_copy(track_b->meta, mLastTrack->meta);
+
+                            mLastTrack->next = track_b;
+                            track_b->next = NULL;
+
+                            auto id = track_b->meta->mFormat->findEntryByName(AMEDIAFORMAT_KEY_CSD_2);
+                            track_b->meta->mFormat->removeEntryAt(id);
+
+                            if (4 == profile || 7 == profile || 8 == profile ) {
+                                AMediaFormat_setString(track_b->meta,
+                                        AMEDIAFORMAT_KEY_MIME, MEDIA_MIMETYPE_VIDEO_HEVC);
+                            } else if (9 == profile) {
+                                AMediaFormat_setString(track_b->meta,
+                                        AMEDIAFORMAT_KEY_MIME, MEDIA_MIMETYPE_VIDEO_AVC);
+                            } else if (10 == profile) {
+                                AMediaFormat_setString(track_b->meta,
+                                        AMEDIAFORMAT_KEY_MIME, MEDIA_MIMETYPE_VIDEO_AV1);
+                            } // Should never get to else part
+
+                            mLastTrack = track_b;
+                        }
+                    }
+                }
             } else if (chunk_type == FOURCC("moov")) {
                 mInitCheck = OK;
 
@@ -1830,6 +1895,11 @@
         case FOURCC("avc1"):
         case FOURCC("hvc1"):
         case FOURCC("hev1"):
+        case FOURCC("dvav"):
+        case FOURCC("dva1"):
+        case FOURCC("dvhe"):
+        case FOURCC("dvh1"):
+        case FOURCC("dav1"):
         case FOURCC("av01"):
         {
             uint8_t buffer[78];
@@ -1984,7 +2054,8 @@
                     // for audio, use 128KB
                     max_size = 1024 * 128;
                 } else if (!strcmp(mime, MEDIA_MIMETYPE_VIDEO_AVC)
-                        || !strcmp(mime, MEDIA_MIMETYPE_VIDEO_HEVC)) {
+                        || !strcmp(mime, MEDIA_MIMETYPE_VIDEO_HEVC)
+                        || !strcmp(mime, MEDIA_MIMETYPE_VIDEO_DOLBY_VISION)) {
                     // AVC & HEVC requires compression ratio of at least 2, and uses
                     // macroblocks
                     max_size = ((width + 15) / 16) * ((height + 15) / 16) * 192;
@@ -2315,6 +2386,30 @@
             *offset += chunk_size;
             break;
         }
+        case FOURCC("dvcC"):
+        case FOURCC("dvvC"): {
+            auto buffer = heapbuffer<uint8_t>(chunk_data_size);
+
+            if (buffer.get() == NULL) {
+                ALOGE("b/28471206");
+                return NO_MEMORY;
+            }
+
+            if (mDataSource->readAt(data_offset, buffer.get(), chunk_data_size) < chunk_data_size) {
+                return ERROR_IO;
+            }
+
+            if (mLastTrack == NULL)
+                return ERROR_MALFORMED;
+
+            AMediaFormat_setBuffer(mLastTrack->meta, AMEDIAFORMAT_KEY_CSD_2,
+                                   buffer.get(), chunk_data_size);
+            AMediaFormat_setString(mLastTrack->meta, AMEDIAFORMAT_KEY_MIME,
+                                   MEDIA_MIMETYPE_VIDEO_DOLBY_VISION);
+
+            *offset += chunk_size;
+            break;
+        }
         case FOURCC("d263"):
         {
             *offset += chunk_size;
@@ -4127,7 +4222,20 @@
         if (!strcasecmp(mime, MEDIA_MIMETYPE_IMAGE_ANDROID_HEIC)) {
             itemTable = mItemTable;
         }
-    } else if (!strcasecmp(mime, MEDIA_MIMETYPE_VIDEO_AV1)) {
+    } else if (!strcasecmp(mime, MEDIA_MIMETYPE_VIDEO_DOLBY_VISION)) {
+        void *data;
+        size_t size;
+        if (!AMediaFormat_getBuffer(track->meta, AMEDIAFORMAT_KEY_CSD_2, &data, &size)) {
+            return NULL;
+        }
+
+        const uint8_t *ptr = (const uint8_t *)data;
+
+        // dv_major.dv_minor Should be 1.0 or 2.1
+        if (size != 24 || ((ptr[0] != 1 || ptr[1] != 0) && (ptr[0] != 2 || ptr[1] != 1))) {
+            return NULL;
+        }
+   } else if (!strcasecmp(mime, MEDIA_MIMETYPE_VIDEO_AV1)) {
         void *data;
         size_t size;
         if (!AMediaFormat_getBuffer(track->meta, AMEDIAFORMAT_KEY_CSD_0, &data, &size)) {
@@ -4172,6 +4280,10 @@
         if (!AMediaFormat_getBuffer(track->meta, AMEDIAFORMAT_KEY_CSD_HEVC, &data, &size)) {
             return ERROR_MALFORMED;
         }
+    } else if (!strcasecmp(mime, MEDIA_MIMETYPE_VIDEO_DOLBY_VISION)) {
+        if (!AMediaFormat_getBuffer(track->meta, AMEDIAFORMAT_KEY_CSD_2, &data, &size)) {
+            return ERROR_MALFORMED;
+        }
     } else if (!strcasecmp(mime, MEDIA_MIMETYPE_VIDEO_AV1)) {
         if (!AMediaFormat_getBuffer(track->meta, AMEDIAFORMAT_KEY_CSD_0, &data, &size)) {
             return ERROR_MALFORMED;
@@ -4659,6 +4771,7 @@
       mCurrentSampleInfoOffsets(NULL),
       mIsAVC(false),
       mIsHEVC(false),
+      mIsDolbyVision(false),
       mIsAC4(false),
       mIsPcm(false),
       mNALLengthSize(0),
@@ -4698,6 +4811,7 @@
     mIsHEVC = !strcasecmp(mime, MEDIA_MIMETYPE_VIDEO_HEVC) ||
               !strcasecmp(mime, MEDIA_MIMETYPE_IMAGE_ANDROID_HEIC);
     mIsAC4 = !strcasecmp(mime, MEDIA_MIMETYPE_AUDIO_AC4);
+    mIsDolbyVision = !strcasecmp(mime, MEDIA_MIMETYPE_VIDEO_DOLBY_VISION);
 
     if (mIsAVC) {
         void *data;
@@ -4722,6 +4836,42 @@
         CHECK_EQ((unsigned)ptr[0], 1u);  // configurationVersion == 1
 
         mNALLengthSize = 1 + (ptr[14 + 7] & 3);
+    } else if (mIsDolbyVision) {
+        ALOGV("%s DolbyVision stream detected", __FUNCTION__);
+        void *data;
+        size_t size;
+        CHECK(AMediaFormat_getBuffer(format, AMEDIAFORMAT_KEY_CSD_2, &data, &size));
+
+        const uint8_t *ptr = (const uint8_t *)data;
+
+        CHECK(size == 24);
+
+        // dv_major.dv_minor Should be 1.0 or 2.1
+        CHECK(!((ptr[0] != 1 || ptr[1] != 0) && (ptr[0] != 2 || ptr[1] != 1)));
+
+        const uint8_t profile = ptr[2] >> 1;
+        // profile == (unknown,1,9) --> AVC; profile = (2,3,4,5,6,7,8) --> HEVC;
+        // profile == (10) --> AV1
+        if (profile > 1 && profile < 9) {
+            CHECK(AMediaFormat_getBuffer(format, AMEDIAFORMAT_KEY_CSD_HEVC, &data, &size));
+
+            const uint8_t *ptr = (const uint8_t *)data;
+
+            CHECK(size >= 22);
+            CHECK_EQ((unsigned)ptr[0], 1u);  // configurationVersion == 1
+
+            mNALLengthSize = 1 + (ptr[14 + 7] & 3);
+        } else if (10 == profile) {
+            /* AV1 profile nothing to do */
+        } else {
+            CHECK(AMediaFormat_getBuffer(format, AMEDIAFORMAT_KEY_CSD_AVC, &data, &size));
+            const uint8_t *ptr = (const uint8_t *)data;
+
+            CHECK(size >= 7);
+            CHECK_EQ((unsigned)ptr[0], 1u);  // configurationVersion == 1
+            // The number of bytes used to encode the length of a NAL unit.
+            mNALLengthSize = 1 + (ptr[4] & 3);
+        }
     }
 
     mIsPcm = !strcasecmp(mime, MEDIA_MIMETYPE_AUDIO_RAW);
@@ -5789,7 +5939,7 @@
         }
     }
 
-    if (!mIsAVC && !mIsHEVC && !mIsAC4) {
+    if (!mIsAVC && !mIsHEVC && !(mIsDolbyVision && mNALLengthSize) && !mIsAC4) {
         if (newBuffer) {
             if (mIsPcm) {
                 // The twos' PCM block reader assumes that all samples has the same size.
@@ -6179,7 +6329,7 @@
         AMediaFormat_setBuffer(bufmeta, AMEDIAFORMAT_KEY_CRYPTO_IV, iv, ivlength);
     }
 
-    if (!mIsAVC && !mIsHEVC) {
+    if (!mIsAVC && !mIsHEVC && !(mIsDolbyVision && mNALLengthSize)) {
         if (newBuffer) {
             if (!isInRange((size_t)0u, mBuffer->size(), size)) {
                 mBuffer->release();
diff --git a/media/libmedia/MidiIoWrapper.cpp b/media/libmedia/MidiIoWrapper.cpp
index d8ef9cf..6d46363 100644
--- a/media/libmedia/MidiIoWrapper.cpp
+++ b/media/libmedia/MidiIoWrapper.cpp
@@ -17,7 +17,6 @@
 //#define LOG_NDEBUG 0
 #define LOG_TAG "MidiIoWrapper"
 #include <utils/Log.h>
-#include <utils/RefBase.h>
 
 #include <sys/stat.h>
 #include <fcntl.h>
@@ -50,7 +49,7 @@
     mDataSource = nullptr;
 }
 
-class DataSourceUnwrapper : public DataSourceBase {
+class DataSourceUnwrapper {
 
 public:
     explicit DataSourceUnwrapper(CDataSource *csource) {
diff --git a/media/libstagefright/ACodec.cpp b/media/libstagefright/ACodec.cpp
index b282c4a..0ca20b9 100644
--- a/media/libstagefright/ACodec.cpp
+++ b/media/libstagefright/ACodec.cpp
@@ -6927,7 +6927,7 @@
         }
     }
 
-    if (mCodec->mMaxPtsGapUs != 0LL) {
+    if (mCodec->mIsVideo && mCodec->mMaxPtsGapUs != 0LL) {
         OMX_PARAM_U32TYPE maxPtsGapParams;
         InitOMXParams(&maxPtsGapParams);
         maxPtsGapParams.nPortIndex = kPortIndexInput;
diff --git a/media/libstagefright/MPEG4Writer.cpp b/media/libstagefright/MPEG4Writer.cpp
index f130c9b..bf4e7de 100644
--- a/media/libstagefright/MPEG4Writer.cpp
+++ b/media/libstagefright/MPEG4Writer.cpp
@@ -2213,8 +2213,10 @@
     } else if (!strcasecmp(mime, MEDIA_MIMETYPE_VIDEO_HEVC) ||
                !strcasecmp(mime, MEDIA_MIMETYPE_IMAGE_ANDROID_HEIC)) {
         mMeta->findData(kKeyHVCC, &type, &data, &size);
-    } else if (!strcasecmp(mime, MEDIA_MIMETYPE_VIDEO_MPEG4)
-            || !strcasecmp(mime, MEDIA_MIMETYPE_AUDIO_AAC)) {
+    } else if (!strcasecmp(mime, MEDIA_MIMETYPE_VIDEO_DOLBY_VISION)) {
+        mMeta->findData(kKeyDVCC, &type, &data, &size);
+    } else if (!strcasecmp(mime, MEDIA_MIMETYPE_VIDEO_MPEG4) ||
+               !strcasecmp(mime, MEDIA_MIMETYPE_AUDIO_AAC)) {
         if (mMeta->findData(kKeyESDS, &type, &data, &size)) {
             ESDS esds(data, size);
             if (esds.getCodecSpecificInfo(&data, &size) == OK &&
diff --git a/media/libstagefright/Utils.cpp b/media/libstagefright/Utils.cpp
index bda6053..ac4d087 100644
--- a/media/libstagefright/Utils.cpp
+++ b/media/libstagefright/Utils.cpp
@@ -227,6 +227,68 @@
     }
 }
 
+static void parseDolbyVisionProfileLevelFromDvcc(const uint8_t *ptr, size_t size, sp<AMessage> &format) {
+    // dv_major.dv_minor Should be 1.0 or 2.1
+    if (size != 24 || ((ptr[0] != 1 || ptr[1] != 0) && (ptr[0] != 2 || ptr[1] != 1))) {
+        ALOGV("Size %zu, dv_major %d, dv_minor %d", size, ptr[0], ptr[1]);
+        return;
+    }
+
+    const uint8_t profile = ptr[2] >> 1;
+    const uint8_t level = ((ptr[2] & 0x1) << 5) | ((ptr[3] >> 3) & 0x1f);
+    const uint8_t rpu_present_flag = (ptr[3] >> 2) & 0x01;
+    const uint8_t el_present_flag = (ptr[3] >> 1) & 0x01;
+    const uint8_t bl_present_flag = (ptr[3] & 0x01);
+    const int32_t bl_compatibility_id = (int32_t)(ptr[4] >> 4);
+
+    ALOGV("profile-level-compatibility value in dv(c|v)c box %d-%d-%d",
+          profile, level, bl_compatibility_id);
+
+    // All Dolby Profiles will have profile and level info in MediaFormat
+    // Profile 8 and 9 will have bl_compatibility_id too.
+    const static ALookup<uint8_t, OMX_VIDEO_DOLBYVISIONPROFILETYPE> profiles{
+        {1, OMX_VIDEO_DolbyVisionProfileDvavPen},
+        {3, OMX_VIDEO_DolbyVisionProfileDvheDen},
+        {4, OMX_VIDEO_DolbyVisionProfileDvheDtr},
+        {5, OMX_VIDEO_DolbyVisionProfileDvheStn},
+        {6, OMX_VIDEO_DolbyVisionProfileDvheDth},
+        {7, OMX_VIDEO_DolbyVisionProfileDvheDtb},
+        {8, OMX_VIDEO_DolbyVisionProfileDvheSt},
+        {9, OMX_VIDEO_DolbyVisionProfileDvavSe},
+        {10, OMX_VIDEO_DolbyVisionProfileDvav110},
+    };
+
+    const static ALookup<uint8_t, OMX_VIDEO_DOLBYVISIONLEVELTYPE> levels{
+        {0, OMX_VIDEO_DolbyVisionLevelUnknown},
+        {1, OMX_VIDEO_DolbyVisionLevelHd24},
+        {2, OMX_VIDEO_DolbyVisionLevelHd30},
+        {3, OMX_VIDEO_DolbyVisionLevelFhd24},
+        {4, OMX_VIDEO_DolbyVisionLevelFhd30},
+        {5, OMX_VIDEO_DolbyVisionLevelFhd60},
+        {6, OMX_VIDEO_DolbyVisionLevelUhd24},
+        {7, OMX_VIDEO_DolbyVisionLevelUhd30},
+        {8, OMX_VIDEO_DolbyVisionLevelUhd48},
+        {9, OMX_VIDEO_DolbyVisionLevelUhd60},
+    };
+    // set rpuAssoc
+    if (rpu_present_flag && el_present_flag && !bl_present_flag) {
+        format->setInt32("rpuAssoc", 1);
+    }
+    // set profile & level if they are recognized
+    OMX_VIDEO_DOLBYVISIONPROFILETYPE codecProfile;
+    OMX_VIDEO_DOLBYVISIONLEVELTYPE codecLevel;
+    if (profiles.map(profile, &codecProfile)) {
+        format->setInt32("profile", codecProfile);
+        if (codecProfile == OMX_VIDEO_DolbyVisionProfileDvheSt ||
+            codecProfile == OMX_VIDEO_DolbyVisionProfileDvavSe) {
+            format->setInt32("bl_compatibility_id", bl_compatibility_id);
+        }
+        if (levels.map(level, &codecLevel)) {
+            format->setInt32("level", codecLevel);
+        }
+    }
+}
+
 static void parseH263ProfileLevelFromD263(const uint8_t *ptr, size_t size, sp<AMessage> &format) {
     if (size < 7) {
         return;
@@ -1411,6 +1473,12 @@
         msg->setBuffer("csd-0", buffer);
     }
 
+    if (meta->findData(kKeyDVCC, &type, &data, &size)) {
+        const uint8_t *ptr = (const uint8_t *)data;
+        ALOGV("DV: calling parseDolbyVisionProfileLevelFromDvcc with data size %zu", size);
+        parseDolbyVisionProfileLevelFromDvcc(ptr, size, msg);
+    }
+
     *format = msg;
 
     return OK;
@@ -1839,6 +1907,32 @@
             meta->setData(kKeyHVCC, kTypeHVCC, hvcc.data(), outsize);
         } else if (mime == MEDIA_MIMETYPE_VIDEO_AV1) {
             meta->setData(kKeyAV1C, 0, csd0->data(), csd0->size());
+        } else if (mime == MEDIA_MIMETYPE_VIDEO_DOLBY_VISION) {
+            if (msg->findBuffer("csd-2", &csd2)) {
+                meta->setData(kKeyDVCC, kTypeDVCC, csd2->data(), csd2->size());
+
+                size_t dvcc_size = 1024;
+                uint8_t dvcc[dvcc_size];
+                memcpy(dvcc, csd2->data(), dvcc_size);
+                const uint8_t profile = dvcc[2] >> 1;
+
+                if (profile > 1 && profile < 9) {
+                    std::vector<uint8_t> hvcc(csd0size + 1024);
+                    size_t outsize = reassembleHVCC(csd0, hvcc.data(), hvcc.size(), 4);
+                    meta->setData(kKeyHVCC, kTypeHVCC, hvcc.data(), outsize);
+                } else if (DolbyVisionProfileDvav110 == profile) {
+                    meta->setData(kKeyAV1C, 0, csd0->data(), csd0->size());
+                } else {
+                    sp<ABuffer> csd1;
+                    if (msg->findBuffer("csd-1", &csd1)) {
+                        std::vector<char> avcc(csd0size + csd1->size() + 1024);
+                        size_t outsize = reassembleAVCC(csd0, csd1, avcc.data());
+                        meta->setData(kKeyAVCC, kTypeAVCC, avcc.data(), outsize);
+                    }
+                }
+            } else {
+                ALOGW("We need csd-2!!. %s", msg->debugString().c_str());
+            }
         } else if (mime == MEDIA_MIMETYPE_VIDEO_VP9) {
             meta->setData(kKeyVp9CodecPrivate, 0, csd0->data(), csd0->size());
         } else if (mime == MEDIA_MIMETYPE_AUDIO_OPUS) {
@@ -1885,8 +1979,18 @@
         meta->setData(kKeyStreamHeader, 'mdat', csd0->data(), csd0->size());
     } else if (msg->findBuffer("d263", &csd0)) {
         meta->setData(kKeyD263, kTypeD263, csd0->data(), csd0->size());
-    }
+    } else if (mime == MEDIA_MIMETYPE_VIDEO_DOLBY_VISION && msg->findBuffer("csd-2", &csd2)) {
+        meta->setData(kKeyDVCC, kTypeDVCC, csd2->data(), csd2->size());
 
+        // Remove CSD-2 from the data here to avoid duplicate data in meta
+        meta->remove(kKeyOpaqueCSD2);
+
+        if (msg->findBuffer("csd-avc", &csd0)) {
+            meta->setData(kKeyAVCC, kTypeAVCC, csd0->data(), csd0->size());
+        } else if (msg->findBuffer("csd-hevc", &csd0)) {
+            meta->setData(kKeyHVCC, kTypeHVCC, csd0->data(), csd0->size());
+        }
+    }
     // XXX TODO add whatever other keys there are
 
 #if 0
diff --git a/media/libstagefright/include/media/stagefright/MediaCodecConstants.h b/media/libstagefright/include/media/stagefright/MediaCodecConstants.h
index 50d7724..16e207d 100644
--- a/media/libstagefright/include/media/stagefright/MediaCodecConstants.h
+++ b/media/libstagefright/include/media/stagefright/MediaCodecConstants.h
@@ -510,22 +510,24 @@
 constexpr int32_t DolbyVisionProfileDvheStn = 0x20;
 constexpr int32_t DolbyVisionProfileDvheDth = 0x40;
 constexpr int32_t DolbyVisionProfileDvheDtb = 0x80;
-constexpr int32_t DolbyVisionProfileDvheSt = 0x100;
-constexpr int32_t DolbyVisionProfileDvavSe = 0x200;
+constexpr int32_t DolbyVisionProfileDvheSt  = 0x100;
+constexpr int32_t DolbyVisionProfileDvavSe  = 0x200;
+constexpr int32_t DolbyVisionProfileDvav110 = 0x400;
 
 inline static const char *asString_DolbyVisionProfile(int32_t i, const char *def = "??") {
     switch (i) {
-        case DolbyVisionProfileDvavPer: return "DvavPer";
-        case DolbyVisionProfileDvavPen: return "DvavPen";
-        case DolbyVisionProfileDvheDer: return "DvheDer";
-        case DolbyVisionProfileDvheDen: return "DvheDen";
-        case DolbyVisionProfileDvheDtr: return "DvheDtr";
-        case DolbyVisionProfileDvheStn: return "DvheStn";
-        case DolbyVisionProfileDvheDth: return "DvheDth";
-        case DolbyVisionProfileDvheDtb: return "DvheDtb";
-        case DolbyVisionProfileDvheSt:  return "DvheSt";
-        case DolbyVisionProfileDvavSe:  return "DvavSe";
-        default:                        return def;
+        case DolbyVisionProfileDvavPer:  return "DvavPer";
+        case DolbyVisionProfileDvavPen:  return "DvavPen";
+        case DolbyVisionProfileDvheDer:  return "DvheDer";
+        case DolbyVisionProfileDvheDen:  return "DvheDen";
+        case DolbyVisionProfileDvheDtr:  return "DvheDtr";
+        case DolbyVisionProfileDvheStn:  return "DvheStn";
+        case DolbyVisionProfileDvheDth:  return "DvheDth";
+        case DolbyVisionProfileDvheDtb:  return "DvheDtb";
+        case DolbyVisionProfileDvheSt:   return "DvheSt";
+        case DolbyVisionProfileDvavSe:   return "DvavSe";
+        case DolbyVisionProfileDvav110:  return "Dav110";
+        default:                         return def;
     }
 }
 
@@ -774,6 +776,7 @@
 constexpr char KEY_LANGUAGE[] = "language";
 constexpr char KEY_LATENCY[] = "latency";
 constexpr char KEY_LEVEL[] = "level";
+constexpr char KEY_LOW_LATENCY[] = "low-latency";
 constexpr char KEY_MAX_B_FRAMES[] = "max-bframes";
 constexpr char KEY_MAX_BIT_RATE[] = "max-bitrate";
 constexpr char KEY_MAX_FPS_TO_ENCODER[] = "max-fps-to-encoder";
diff --git a/media/libstagefright/include/media/stagefright/MetaDataBase.h b/media/libstagefright/include/media/stagefright/MetaDataBase.h
index 659bd5b..e17093a 100644
--- a/media/libstagefright/include/media/stagefright/MetaDataBase.h
+++ b/media/libstagefright/include/media/stagefright/MetaDataBase.h
@@ -59,6 +59,7 @@
     kKeyAACProfile        = 'aacp',  // int32_t
     kKeyAVCC              = 'avcc',  // raw data
     kKeyHVCC              = 'hvcc',  // raw data
+    kKeyDVCC              = 'dvcc',  // raw data
     kKeyAV1C              = 'av1c',  // raw data
     kKeyThumbnailHVCC     = 'thvc',  // raw data
     kKeyD263              = 'd263',  // raw data
@@ -245,6 +246,7 @@
     kTypeAVCC        = 'avcc',
     kTypeHVCC        = 'hvcc',
     kTypeAV1C        = 'av1c',
+    kTypeDVCC        = 'dvcc',
     kTypeD263        = 'd263',
 };
 
diff --git a/media/utils/TimeCheck.cpp b/media/utils/TimeCheck.cpp
index 265a232..96f7802 100644
--- a/media/utils/TimeCheck.cpp
+++ b/media/utils/TimeCheck.cpp
@@ -82,10 +82,10 @@
         if (waitTimeNs > 0) {
             status = mCond.waitRelative(mMutex, waitTimeNs);
         }
-    }
-    if (status != NO_ERROR) {
-        LOG_EVENT_STRING(LOGTAG_AUDIO_BINDER_TIMEOUT, tag);
-        LOG_ALWAYS_FATAL("TimeCheck timeout for %s", tag);
+        if (status != NO_ERROR) {
+            LOG_EVENT_STRING(LOGTAG_AUDIO_BINDER_TIMEOUT, tag);
+            LOG_ALWAYS_FATAL("TimeCheck timeout for %s", tag);
+        }
     }
     return true;
 }
diff --git a/services/camera/libcameraservice/CameraService.cpp b/services/camera/libcameraservice/CameraService.cpp
index 94642f2..a02178e 100644
--- a/services/camera/libcameraservice/CameraService.cpp
+++ b/services/camera/libcameraservice/CameraService.cpp
@@ -268,8 +268,12 @@
         const std::vector<std::string> &normalDeviceIds) {
     mNormalDeviceIdsWithoutSystemCamera.clear();
     for (auto &deviceId : normalDeviceIds) {
-        if (getSystemCameraKind(String8(deviceId.c_str())) ==
-                SystemCameraKind::SYSTEM_ONLY_CAMERA) {
+        SystemCameraKind deviceKind = SystemCameraKind::PUBLIC;
+        if (getSystemCameraKind(String8(deviceId.c_str()), &deviceKind) != OK) {
+            ALOGE("%s: Invalid camera id %s, skipping", __FUNCTION__, deviceId.c_str());
+            continue;
+        }
+        if (deviceKind == SystemCameraKind::SYSTEM_ONLY_CAMERA) {
             // All system camera ids will necessarily come after public camera
             // device ids as per the HAL interface contract.
             break;
@@ -280,6 +284,16 @@
               mNormalDeviceIdsWithoutSystemCamera.size());
 }
 
+status_t CameraService::getSystemCameraKind(const String8& cameraId, SystemCameraKind *kind) const {
+    auto state = getCameraState(cameraId);
+    if (state != nullptr) {
+        *kind = state->getSystemCameraKind();
+        return OK;
+    }
+    // Hidden physical camera ids won't have CameraState
+    return mCameraProviderManager->getSystemCameraKind(cameraId.c_str(), kind);
+}
+
 void CameraService::updateCameraNumAndIds() {
     Mutex::Autolock l(mServiceLock);
     std::pair<int, int> systemAndNonSystemCameras = mCameraProviderManager->getCameraCount();
@@ -296,10 +310,16 @@
     std::string cameraId(id.c_str());
     hardware::camera::common::V1_0::CameraResourceCost cost;
     status_t res = mCameraProviderManager->getResourceCost(cameraId, &cost);
+    SystemCameraKind deviceKind = SystemCameraKind::PUBLIC;
     if (res != OK) {
         ALOGE("Failed to query device resource cost: %s (%d)", strerror(-res), res);
         return;
     }
+    res = mCameraProviderManager->getSystemCameraKind(cameraId, &deviceKind);
+    if (res != OK) {
+        ALOGE("Failed to query device kind: %s (%d)", strerror(-res), res);
+        return;
+    }
     std::set<String8> conflicting;
     for (size_t i = 0; i < cost.conflictingDevices.size(); i++) {
         conflicting.emplace(String8(cost.conflictingDevices[i].c_str()));
@@ -308,7 +328,7 @@
     {
         Mutex::Autolock lock(mCameraStatesLock);
         mCameraStates.emplace(id, std::make_shared<CameraState>(id, cost.resourceCost,
-                                                                conflicting));
+                                                                conflicting, deviceKind));
     }
 
     if (mFlashlight->hasFlashUnit(id)) {
@@ -594,7 +614,12 @@
                 "characteristics for device %s: %s (%d)", String8(cameraId).string(),
                 strerror(-res), res);
     }
-
+    SystemCameraKind deviceKind = SystemCameraKind::PUBLIC;
+    if (getSystemCameraKind(String8(cameraId), &deviceKind) != OK) {
+        ALOGE("%s: Invalid camera id %s, skipping", __FUNCTION__, String8(cameraId).string());
+        return STATUS_ERROR_FMT(ERROR_INVALID_OPERATION, "Unable to retrieve camera kind "
+                "for device %s", String8(cameraId).string());
+    }
     int callingPid = CameraThreadState::getCallingPid();
     int callingUid = CameraThreadState::getCallingUid();
     std::vector<int32_t> tagsRemoved;
@@ -602,7 +627,7 @@
     // android.permission.CAMERA is required. If android.permission.SYSTEM_CAMERA was needed,
     // it would've already been checked in shouldRejectSystemCameraConnection.
     if ((callingPid != getpid()) &&
-            (getSystemCameraKind(String8(cameraId)) != SystemCameraKind::SYSTEM_ONLY_CAMERA) &&
+            (deviceKind != SystemCameraKind::SYSTEM_ONLY_CAMERA) &&
             !checkPermission(sCameraPermission, callingPid, callingUid)) {
         res = cameraInfo->removePermissionEntries(
                 mCameraProviderManager->getProviderTagIdLocked(String8(cameraId).string()),
@@ -1049,11 +1074,19 @@
         return STATUS_ERROR_FMT(ERROR_DISCONNECTED, "No camera device with ID \"%s\" is"
                                 "available", cameraId.string());
     }
+    SystemCameraKind deviceKind = SystemCameraKind::PUBLIC;
+    if (getSystemCameraKind(cameraId, &deviceKind) != OK) {
+        ALOGE("%s: Invalid camera id %s, skipping", __FUNCTION__, cameraId.string());
+        return STATUS_ERROR_FMT(ERROR_ILLEGAL_ARGUMENT, "No camera device with ID \"%s\""
+                "found while trying to query device kind", cameraId.string());
+
+    }
+
     // If it's not calling from cameraserver, check the permission if the
     // device isn't a system only camera (shouldRejectSystemCameraConnection already checks for
     // android.permission.SYSTEM_CAMERA for system only camera devices).
     if (callingPid != getpid() &&
-                (getSystemCameraKind(cameraId) != SystemCameraKind::SYSTEM_ONLY_CAMERA) &&
+                (deviceKind != SystemCameraKind::SYSTEM_ONLY_CAMERA) &&
                 !checkPermission(sCameraPermission, clientPid, clientUid)) {
         ALOGE("Permission Denial: can't use the camera pid=%d, uid=%d", clientPid, clientUid);
         return STATUS_ERROR_FMT(ERROR_PERMISSION_DENIED,
@@ -1407,9 +1440,8 @@
     return ret;
 }
 
-bool CameraService::shouldSkipStatusUpdates(const String8& cameraId, bool isVendorListener,
-        int clientPid, int clientUid) const {
-    SystemCameraKind systemCameraKind = getSystemCameraKind(cameraId);
+bool CameraService::shouldSkipStatusUpdates(SystemCameraKind systemCameraKind,
+        bool isVendorListener, int clientPid, int clientUid) {
     // If the client is not a vendor client, don't add listener if
     //   a) the camera is a publicly hidden secure camera OR
     //   b) the camera is a system only camera and the client doesn't
@@ -1435,7 +1467,11 @@
 
     int cPid = CameraThreadState::getCallingPid();
     int cUid = CameraThreadState::getCallingUid();
-    SystemCameraKind systemCameraKind = getSystemCameraKind(cameraId);
+    SystemCameraKind systemCameraKind = SystemCameraKind::PUBLIC;
+    if (getSystemCameraKind(cameraId, &systemCameraKind) != OK) {
+        ALOGE("%s: Invalid camera id %s, ", __FUNCTION__, cameraId.c_str());
+        return true;
+    }
 
     // (1) Cameraserver trying to connect, accept.
     if (CameraThreadState::getCallingPid() == getpid()) {
@@ -1926,14 +1962,24 @@
     {
         Mutex::Autolock lock(mCameraStatesLock);
         for (auto& i : mCameraStates) {
-            if (shouldSkipStatusUpdates(i.first, isVendorListener, clientPid, clientUid)) {
-                ALOGV("Cannot add public listener for hidden system-only %s for pid %d",
-                      i.first.c_str(), CameraThreadState::getCallingPid());
-                continue;
-            }
             cameraStatuses->emplace_back(i.first, mapToInterface(i.second->getStatus()));
         }
     }
+    // Remove the camera statuses that should be hidden from the client, we do
+    // this after collecting the states in order to avoid holding
+    // mCameraStatesLock and mInterfaceLock (held in getSystemCameraKind()) at
+    // the same time.
+    cameraStatuses->erase(std::remove_if(cameraStatuses->begin(), cameraStatuses->end(),
+                [this, &isVendorListener, &clientPid, &clientUid](const hardware::CameraStatus& s) {
+                    SystemCameraKind deviceKind = SystemCameraKind::PUBLIC;
+                    if (getSystemCameraKind(s.cameraId, &deviceKind) != OK) {
+                        ALOGE("%s: Invalid camera id %s, skipping status update",
+                                __FUNCTION__, s.cameraId.c_str());
+                        return true;
+                    }
+                    return shouldSkipStatusUpdates(deviceKind, isVendorListener, clientPid,
+                            clientUid);}), cameraStatuses->end());
+
 
     /*
      * Immediately signal current torch status to this listener only
@@ -3023,8 +3069,9 @@
 // ----------------------------------------------------------------------------
 
 CameraService::CameraState::CameraState(const String8& id, int cost,
-        const std::set<String8>& conflicting) : mId(id),
-        mStatus(StatusInternal::NOT_PRESENT), mCost(cost), mConflicting(conflicting) {}
+        const std::set<String8>& conflicting, SystemCameraKind systemCameraKind) : mId(id),
+        mStatus(StatusInternal::NOT_PRESENT), mCost(cost), mConflicting(conflicting),
+        mSystemCameraKind(systemCameraKind) {}
 
 CameraService::CameraState::~CameraState() {}
 
@@ -3053,6 +3100,10 @@
     return mId;
 }
 
+SystemCameraKind CameraService::CameraState::getSystemCameraKind() const {
+    return mSystemCameraKind;
+}
+
 // ----------------------------------------------------------------------------
 //                  ClientEventListener
 // ----------------------------------------------------------------------------
@@ -3391,9 +3442,16 @@
         return;
     }
 
+    // Avoid calling getSystemCameraKind() with mStatusListenerLock held (b/141756275)
+    SystemCameraKind deviceKind = SystemCameraKind::PUBLIC;
+    if (getSystemCameraKind(cameraId, &deviceKind) != OK) {
+        ALOGE("%s: Invalid camera id %s, skipping", __FUNCTION__, cameraId.string());
+        return;
+    }
+
     // Update the status for this camera state, then send the onStatusChangedCallbacks to each
     // of the listeners with both the mStatusStatus and mStatusListenerLock held
-    state->updateStatus(status, cameraId, rejectSourceStates, [this]
+    state->updateStatus(status, cameraId, rejectSourceStates, [this, &deviceKind]
             (const String8& cameraId, StatusInternal status) {
 
             if (status != StatusInternal::ENUMERATING) {
@@ -3415,7 +3473,7 @@
             Mutex::Autolock lock(mStatusListenerLock);
 
             for (auto& listener : mListenerList) {
-                if (shouldSkipStatusUpdates(cameraId, listener->isVendorListener(),
+                if (shouldSkipStatusUpdates(deviceKind, listener->isVendorListener(),
                         listener->getListenerPid(), listener->getListenerUid())) {
                     ALOGV("Skipping camera discovery callback for system-only camera %s",
                             cameraId.c_str());
diff --git a/services/camera/libcameraservice/CameraService.h b/services/camera/libcameraservice/CameraService.h
index 058d57e..8765fbf 100644
--- a/services/camera/libcameraservice/CameraService.h
+++ b/services/camera/libcameraservice/CameraService.h
@@ -490,7 +490,8 @@
          * Make a new CameraState and set the ID, cost, and conflicting devices using the values
          * returned in the HAL's camera_info struct for each device.
          */
-        CameraState(const String8& id, int cost, const std::set<String8>& conflicting);
+        CameraState(const String8& id, int cost, const std::set<String8>& conflicting,
+                SystemCameraKind deviceKind);
         virtual ~CameraState();
 
         /**
@@ -542,6 +543,11 @@
          */
         String8 getId() const;
 
+        /**
+         * Return the kind (SystemCameraKind) of this camera device.
+         */
+        SystemCameraKind getSystemCameraKind() const;
+
     private:
         const String8 mId;
         StatusInternal mStatus; // protected by mStatusLock
@@ -549,6 +555,7 @@
         std::set<String8> mConflicting;
         mutable Mutex mStatusLock;
         CameraParameters mShimParams;
+        const SystemCameraKind mSystemCameraKind;
     }; // class CameraState
 
     // Observer for UID lifecycle enforcing that UIDs in idle
@@ -660,12 +667,14 @@
     // Should a device status update be skipped for a particular camera device ? (this can happen
     // under various conditions. For example if a camera device is advertised as
     // system only or hidden secure camera, amongst possible others.
-    bool shouldSkipStatusUpdates(const String8& cameraId, bool isVendorListener, int clientPid,
-            int clientUid) const;
+    static bool shouldSkipStatusUpdates(SystemCameraKind systemCameraKind, bool isVendorListener,
+            int clientPid, int clientUid);
 
-    inline SystemCameraKind getSystemCameraKind(const String8& cameraId) const {
-        return mCameraProviderManager->getSystemCameraKind(cameraId.c_str());
-    }
+    // Gets the kind of camera device (i.e public, hidden secure or system only)
+    // getSystemCameraKind() needs mInterfaceMutex which might lead to deadlocks
+    // if held along with mStatusListenerLock (depending on lock ordering, b/141756275), it is
+    // recommended that we don't call this function with mStatusListenerLock held.
+    status_t getSystemCameraKind(const String8& cameraId, SystemCameraKind *kind) const;
 
     // Update the set of API1Compatible camera devices without including system
     // cameras and secure cameras. This is used for hiding system only cameras
diff --git a/services/camera/libcameraservice/common/CameraProviderManager.cpp b/services/camera/libcameraservice/common/CameraProviderManager.cpp
index 974026c..608521a 100644
--- a/services/camera/libcameraservice/common/CameraProviderManager.cpp
+++ b/services/camera/libcameraservice/common/CameraProviderManager.cpp
@@ -110,7 +110,12 @@
     int publicCameraCount = 0;
     for (auto& provider : mProviders) {
         for (auto &id : provider->mUniqueCameraIds) {
-            switch(getSystemCameraKindLocked(id)) {
+            SystemCameraKind deviceKind = SystemCameraKind::PUBLIC;
+            if (getSystemCameraKindLocked(id, &deviceKind) != OK) {
+                ALOGE("%s: Invalid camera id %s, skipping", __FUNCTION__, id.c_str());
+                continue;
+            }
+            switch(deviceKind) {
                 case SystemCameraKind::PUBLIC:
                     publicCameraCount++;
                     break;
@@ -140,7 +145,12 @@
         std::vector<std::string>& publicDeviceIds,
         std::vector<std::string>& systemDeviceIds) const {
     for (auto &deviceId : deviceIds) {
-        if (getSystemCameraKindLocked(deviceId) == SystemCameraKind::SYSTEM_ONLY_CAMERA) {
+        SystemCameraKind deviceKind = SystemCameraKind::PUBLIC;
+        if (getSystemCameraKindLocked(deviceId, &deviceKind) != OK) {
+            ALOGE("%s: Invalid camera id %s, skipping", __FUNCTION__, deviceId.c_str());
+            continue;
+        }
+        if (deviceKind == SystemCameraKind::SYSTEM_ONLY_CAMERA) {
             systemDeviceIds.push_back(deviceId);
         } else {
             publicDeviceIds.push_back(deviceId);
@@ -158,9 +168,13 @@
         // Secure cameras should not be exposed through camera 1 api
         providerDeviceIds.erase(std::remove_if(providerDeviceIds.begin(), providerDeviceIds.end(),
                 [this](const std::string& s) {
-                bool rem = this->getSystemCameraKindLocked(s) ==
-                        SystemCameraKind::HIDDEN_SECURE_CAMERA;
-                return rem;}), providerDeviceIds.end());
+                SystemCameraKind deviceKind = SystemCameraKind::PUBLIC;
+                if (getSystemCameraKindLocked(s, &deviceKind) != OK) {
+                    ALOGE("%s: Invalid camera id %s, skipping", __FUNCTION__, s.c_str());
+                    return true;
+                }
+                return deviceKind == SystemCameraKind::HIDDEN_SECURE_CAMERA;}),
+                providerDeviceIds.end());
         // API1 app doesn't handle logical and physical camera devices well. So
         // for each camera facing, only take the first id advertised by HAL in
         // all [logical, physical1, physical2, ...] id combos, and filter out the rest.
@@ -1089,26 +1103,45 @@
     return deviceInfo->mIsLogicalCamera;
 }
 
-SystemCameraKind CameraProviderManager::getSystemCameraKind(const std::string& id) const {
+status_t CameraProviderManager::getSystemCameraKind(const std::string& id,
+        SystemCameraKind *kind) const {
     std::lock_guard<std::mutex> lock(mInterfaceMutex);
-    return getSystemCameraKindLocked(id);
+    return getSystemCameraKindLocked(id, kind);
 }
 
-SystemCameraKind CameraProviderManager::getSystemCameraKindLocked(const std::string& id) const {
+status_t CameraProviderManager::getSystemCameraKindLocked(const std::string& id,
+        SystemCameraKind *kind) const {
     auto deviceInfo = findDeviceInfoLocked(id);
-    if (deviceInfo == nullptr) {
-        return SystemCameraKind::PUBLIC;
+    if (deviceInfo != nullptr) {
+        *kind = deviceInfo->mSystemCameraKind;
+        return OK;
     }
-    return deviceInfo->mSystemCameraKind;
+    // If this is a hidden physical camera, we should return what kind of
+    // camera the enclosing logical camera is.
+    auto isHiddenAndParent = isHiddenPhysicalCameraInternal(id);
+    if (isHiddenAndParent.first) {
+        LOG_ALWAYS_FATAL_IF(id == isHiddenAndParent.second->mId,
+                "%s: hidden physical camera id %s and enclosing logical camera id %s are the same",
+                __FUNCTION__, id.c_str(), isHiddenAndParent.second->mId.c_str());
+        return getSystemCameraKindLocked(isHiddenAndParent.second->mId, kind);
+    }
+    // Neither a hidden physical camera nor a logical camera
+    return NAME_NOT_FOUND;
 }
 
-bool CameraProviderManager::isHiddenPhysicalCamera(const std::string& cameraId) {
+bool CameraProviderManager::isHiddenPhysicalCamera(const std::string& cameraId) const {
+    return isHiddenPhysicalCameraInternal(cameraId).first;
+}
+
+std::pair<bool, CameraProviderManager::ProviderInfo::DeviceInfo *>
+CameraProviderManager::isHiddenPhysicalCameraInternal(const std::string& cameraId) const {
+    auto falseRet = std::make_pair(false, nullptr);
     for (auto& provider : mProviders) {
         for (auto& deviceInfo : provider->mDevices) {
             if (deviceInfo->mId == cameraId) {
                 // cameraId is found in public camera IDs advertised by the
                 // provider.
-                return false;
+                return falseRet;
             }
         }
     }
@@ -1120,7 +1153,7 @@
             if (res != OK) {
                 ALOGE("%s: Failed to getCameraCharacteristics for id %s", __FUNCTION__,
                         deviceInfo->mId.c_str());
-                return false;
+                return falseRet;
             }
 
             std::vector<std::string> physicalIds;
@@ -1132,16 +1165,16 @@
                     if (deviceVersion < CAMERA_DEVICE_API_VERSION_3_5) {
                         ALOGE("%s: Wrong deviceVersion %x for hiddenPhysicalCameraId %s",
                                 __FUNCTION__, deviceVersion, cameraId.c_str());
-                        return false;
+                        return falseRet;
                     } else {
-                        return true;
+                        return std::make_pair(true, deviceInfo.get());
                     }
                 }
             }
         }
     }
 
-    return false;
+    return falseRet;
 }
 
 status_t CameraProviderManager::addProviderLocked(const std::string& newProvider) {
diff --git a/services/camera/libcameraservice/common/CameraProviderManager.h b/services/camera/libcameraservice/common/CameraProviderManager.h
index f4cf667..4112711 100644
--- a/services/camera/libcameraservice/common/CameraProviderManager.h
+++ b/services/camera/libcameraservice/common/CameraProviderManager.h
@@ -292,8 +292,8 @@
      */
     bool isLogicalCamera(const std::string& id, std::vector<std::string>* physicalCameraIds);
 
-    SystemCameraKind getSystemCameraKind(const std::string& id) const;
-    bool isHiddenPhysicalCamera(const std::string& cameraId);
+    status_t getSystemCameraKind(const std::string& id, SystemCameraKind *kind) const;
+    bool isHiddenPhysicalCamera(const std::string& cameraId) const;
 
     static const float kDepthARTolerance;
 private:
@@ -616,7 +616,8 @@
             CameraMetadata* characteristics) const;
     void filterLogicalCameraIdsLocked(std::vector<std::string>& deviceIds) const;
 
-    SystemCameraKind getSystemCameraKindLocked(const std::string& id) const;
+    status_t getSystemCameraKindLocked(const std::string& id, SystemCameraKind *kind) const;
+    std::pair<bool, ProviderInfo::DeviceInfo *> isHiddenPhysicalCameraInternal(const std::string& cameraId) const;
 
     void collectDeviceIdsLocked(const std::vector<std::string> deviceIds,
             std::vector<std::string>& normalDeviceIds,