Merge "Add log session id to MediaParser" into sc-dev
diff --git a/apex/mediatranscoding.rc b/apex/mediatranscoding.rc
index 24306a2..ae9f8ba 100644
--- a/apex/mediatranscoding.rc
+++ b/apex/mediatranscoding.rc
@@ -8,4 +8,5 @@
     ioprio rt 4
     # Restrict to little cores only with system-background cpuset.
     writepid /dev/cpuset/system-background/tasks
+    interface aidl media.transcoding
     disabled
diff --git a/camera/Android.bp b/camera/Android.bp
index 2c01496..6878c20 100644
--- a/camera/Android.bp
+++ b/camera/Android.bp
@@ -119,6 +119,8 @@
         "aidl/android/hardware/camera2/ICameraDeviceCallbacks.aidl",
         "aidl/android/hardware/camera2/ICameraDeviceUser.aidl",
         "aidl/android/hardware/camera2/ICameraOfflineSession.aidl",
+        "aidl/android/hardware/camera2/ICameraInjectionCallback.aidl",
+        "aidl/android/hardware/camera2/ICameraInjectionSession.aidl",
     ],
     path: "aidl",
 }
diff --git a/camera/aidl/android/hardware/ICameraService.aidl b/camera/aidl/android/hardware/ICameraService.aidl
index 459ad15..873d738 100644
--- a/camera/aidl/android/hardware/ICameraService.aidl
+++ b/camera/aidl/android/hardware/ICameraService.aidl
@@ -20,6 +20,8 @@
 import android.hardware.ICameraClient;
 import android.hardware.camera2.ICameraDeviceUser;
 import android.hardware.camera2.ICameraDeviceCallbacks;
+import android.hardware.camera2.ICameraInjectionCallback;
+import android.hardware.camera2.ICameraInjectionSession;
 import android.hardware.camera2.params.VendorTagDescriptor;
 import android.hardware.camera2.params.VendorTagDescriptorCache;
 import android.hardware.camera2.utils.ConcurrentCameraIdCombination;
@@ -161,6 +163,9 @@
     boolean supportsCameraApi(String cameraId, int apiVersion);
     // Determines if a cameraId is a hidden physical camera of a logical multi-camera.
     boolean isHiddenPhysicalCamera(String cameraId);
+    // Inject the external camera to replace the internal camera session.
+    ICameraInjectionSession injectCamera(String packageName, String internalCamId,
+            String externalCamId, in ICameraInjectionCallback CameraInjectionCallback);
 
     void setTorchMode(String cameraId, boolean enabled, IBinder clientBinder);
 
diff --git a/camera/aidl/android/hardware/camera2/ICameraInjectionCallback.aidl b/camera/aidl/android/hardware/camera2/ICameraInjectionCallback.aidl
new file mode 100644
index 0000000..9791352
--- /dev/null
+++ b/camera/aidl/android/hardware/camera2/ICameraInjectionCallback.aidl
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 2021 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.
+ */
+
+package android.hardware.camera2;
+
+import android.hardware.camera2.ICameraInjectionSession;
+
+/**
+ * Binder interface used to call back the error state injected by the external camera,
+ * and camera service can be switched back to internal camera when binder signals process death.
+ *
+ * @hide
+ */
+interface ICameraInjectionCallback
+{
+    // Error codes for onInjectionError
+    // To indicate all invalid error codes
+    const int ERROR_INJECTION_INVALID_ERROR = -1;
+    // To indicate the camera injection session has encountered a fatal error, such as injection
+    // init failure, configure failure or injecting failure etc.
+    const int ERROR_INJECTION_SESSION = 0;
+    // To indicate the camera service has encountered a fatal error.
+    const int ERROR_INJECTION_SERVICE = 1;
+    // To indicate the injection camera does not support certain camera functions, such as
+    // unsupport stream format, no capture/record function or no multi-camera function etc.
+    // When this error occurs, the default processing is still in the inject state, and the app is
+    // notified to display an error message and a black screen.
+    const int ERROR_INJECTION_UNSUPPORTED = 2;
+
+    oneway void onInjectionError(int errorCode);
+}
diff --git a/camera/aidl/android/hardware/camera2/ICameraInjectionSession.aidl b/camera/aidl/android/hardware/camera2/ICameraInjectionSession.aidl
new file mode 100644
index 0000000..c31c30b
--- /dev/null
+++ b/camera/aidl/android/hardware/camera2/ICameraInjectionSession.aidl
@@ -0,0 +1,23 @@
+/*
+ * Copyright (C) 2021 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.
+ */
+
+package android.hardware.camera2;
+
+/** @hide */
+interface ICameraInjectionSession
+{
+    oneway void stopInjection();
+}
diff --git a/media/codec2/components/raw/C2SoftRawDec.cpp b/media/codec2/components/raw/C2SoftRawDec.cpp
index 31ca705..a03d4e2 100644
--- a/media/codec2/components/raw/C2SoftRawDec.cpp
+++ b/media/codec2/components/raw/C2SoftRawDec.cpp
@@ -87,7 +87,9 @@
                 .withFields({C2F(mPcmEncodingInfo, value).oneOf({
                      C2Config::PCM_16,
                      C2Config::PCM_8,
-                     C2Config::PCM_FLOAT})
+                     C2Config::PCM_FLOAT,
+                     C2Config::PCM_24,
+                     C2Config::PCM_32})
                 })
                 .withSetter((Setter<decltype(*mPcmEncodingInfo)>::StrictValueWithNoDeps))
                 .build());
diff --git a/media/codec2/core/include/C2Config.h b/media/codec2/core/include/C2Config.h
index b312802..8ee5f33 100644
--- a/media/codec2/core/include/C2Config.h
+++ b/media/codec2/core/include/C2Config.h
@@ -1908,7 +1908,9 @@
 C2ENUM(C2Config::pcm_encoding_t, uint32_t,
     PCM_16,
     PCM_8,
-    PCM_FLOAT
+    PCM_FLOAT,
+    PCM_24,
+    PCM_32
 )
 
 typedef C2StreamParam<C2Info, C2SimpleValueStruct<C2Config::pcm_encoding_t>, kParamIndexPcmEncoding>
diff --git a/media/codec2/hidl/plugin/FilterWrapperStub.cpp b/media/codec2/hidl/plugin/FilterWrapperStub.cpp
index 1b94a1a..01ca596 100644
--- a/media/codec2/hidl/plugin/FilterWrapperStub.cpp
+++ b/media/codec2/hidl/plugin/FilterWrapperStub.cpp
@@ -42,10 +42,10 @@
 }
 
 c2_status_t FilterWrapper::createBlockPool(
-        C2PlatformAllocatorStore::id_t,
-        std::shared_ptr<const C2Component>,
-        std::shared_ptr<C2BlockPool> *) {
-    return C2_OMITTED;
+        C2PlatformAllocatorStore::id_t allocatorId,
+        std::shared_ptr<const C2Component> component,
+        std::shared_ptr<C2BlockPool> *pool) {
+    return CreateCodec2BlockPool(allocatorId, component, pool);
 }
 
 }  // namespace android
diff --git a/media/codec2/sfplugin/utils/Codec2Mapper.cpp b/media/codec2/sfplugin/utils/Codec2Mapper.cpp
index 1390642..00bf84f 100644
--- a/media/codec2/sfplugin/utils/Codec2Mapper.cpp
+++ b/media/codec2/sfplugin/utils/Codec2Mapper.cpp
@@ -311,6 +311,8 @@
     { C2Config::PCM_8, kAudioEncodingPcm8bit },
     { C2Config::PCM_16, kAudioEncodingPcm16bit },
     { C2Config::PCM_FLOAT, kAudioEncodingPcmFloat },
+    { C2Config::PCM_24, kAudioEncodingPcm24bitPacked },
+    { C2Config::PCM_32, kAudioEncodingPcm32bit },
 };
 
 ALookup<C2Config::level_t, int32_t> sVp9Levels = {
diff --git a/media/extractors/mp4/MPEG4Extractor.cpp b/media/extractors/mp4/MPEG4Extractor.cpp
index b1d72e8..416884e 100644
--- a/media/extractors/mp4/MPEG4Extractor.cpp
+++ b/media/extractors/mp4/MPEG4Extractor.cpp
@@ -62,6 +62,16 @@
 
 #define ALAC_SPECIFIC_INFO_SIZE (36)
 
+// TODO : Remove the defines once mainline media is built against NDK >= 31.
+// The mp4 extractor is part of mainline and builds against NDK 29 as of
+// writing. These keys are available only from NDK 31:
+#define AMEDIAFORMAT_KEY_MPEGH_PROFILE_LEVEL_INDICATION \
+  "mpegh-profile-level-indication"
+#define AMEDIAFORMAT_KEY_MPEGH_REFERENCE_CHANNEL_LAYOUT \
+  "mpegh-reference-channel-layout"
+#define AMEDIAFORMAT_KEY_MPEGH_COMPATIBLE_SETS \
+  "mpegh-compatible-sets"
+
 namespace android {
 
 enum {
@@ -139,6 +149,7 @@
     bool mIsHEVC;
     bool mIsDolbyVision;
     bool mIsAC4;
+    bool mIsMpegH = false;
     bool mIsPcm;
     size_t mNALLengthSize;
 
@@ -378,6 +389,10 @@
         case FOURCC(".mp3"):
         case 0x6D730055: // "ms U" mp3 audio
             return MEDIA_MIMETYPE_AUDIO_MPEG;
+        case FOURCC("mha1"):
+            return MEDIA_MIMETYPE_AUDIO_MPEGH_MHA1;
+        case FOURCC("mhm1"):
+            return MEDIA_MIMETYPE_AUDIO_MPEGH_MHM1;
         default:
             ALOGW("Unknown fourcc: %c%c%c%c",
                    (fourcc >> 24) & 0xff,
@@ -1778,6 +1793,8 @@
         case FOURCC("fLaC"):
         case FOURCC(".mp3"):
         case 0x6D730055: // "ms U" mp3 audio
+        case FOURCC("mha1"):
+        case FOURCC("mhm1"):
         {
             if (mIsQT && depth >= 1 && mPath[depth - 1] == FOURCC("wave")) {
 
@@ -1977,7 +1994,94 @@
             }
             break;
         }
+        case FOURCC("mhaC"):
+        {
+            // See ISO_IEC_23008-3;2019 MHADecoderConfigurationRecord
+            constexpr uint32_t mhac_header_size = 4 /* size */ + 4 /* boxtype 'mhaC' */
+                    + 1 /* configurationVersion */ + 1 /* mpegh3daProfileLevelIndication */
+                    + 1 /* referenceChannelLayout */ + 2 /* mpegh3daConfigLength */;
+            uint8_t mhac_header[mhac_header_size];
+            off64_t data_offset = *offset;
 
+            if (chunk_size < sizeof(mhac_header)) {
+                return ERROR_MALFORMED;
+            }
+
+            if (mDataSource->readAt(data_offset, mhac_header, sizeof(mhac_header))
+                    < (ssize_t)sizeof(mhac_header)) {
+                return ERROR_IO;
+            }
+
+            //get mpegh3daProfileLevelIndication
+            const uint32_t mpegh3daProfileLevelIndication = mhac_header[9];
+            AMediaFormat_setInt32(mLastTrack->meta,
+                    AMEDIAFORMAT_KEY_MPEGH_PROFILE_LEVEL_INDICATION,
+                    mpegh3daProfileLevelIndication);
+
+             //get referenceChannelLayout
+            const uint32_t referenceChannelLayout = mhac_header[10];
+            AMediaFormat_setInt32(mLastTrack->meta,
+                    AMEDIAFORMAT_KEY_MPEGH_REFERENCE_CHANNEL_LAYOUT,
+                    referenceChannelLayout);
+
+            // get mpegh3daConfigLength
+            const uint32_t mhac_config_size = U16_AT(&mhac_header[11]);
+            if (chunk_size != sizeof(mhac_header) + mhac_config_size) {
+                return ERROR_MALFORMED;
+            }
+
+            data_offset += sizeof(mhac_header);
+            uint8_t mhac_config[mhac_config_size];
+            if (mDataSource->readAt(data_offset, mhac_config, sizeof(mhac_config))
+                    < (ssize_t)sizeof(mhac_config)) {
+                return ERROR_IO;
+            }
+
+            AMediaFormat_setBuffer(mLastTrack->meta,
+                    AMEDIAFORMAT_KEY_CSD_0, mhac_config, sizeof(mhac_config));
+            data_offset += sizeof(mhac_config);
+            *offset = data_offset;
+            break;
+        }
+        case FOURCC("mhaP"):
+        {
+            // FDAmd_2 of ISO_IEC_23008-3;2019 MHAProfileAndLevelCompatibilitySetBox
+            constexpr uint32_t mhap_header_size = 4 /* size */ + 4 /* boxtype 'mhaP' */
+                    + 1 /* numCompatibleSets */;
+
+            uint8_t mhap_header[mhap_header_size];
+            off64_t data_offset = *offset;
+
+            if (chunk_size < (ssize_t)mhap_header_size) {
+                return ERROR_MALFORMED;
+            }
+
+            if (mDataSource->readAt(data_offset, mhap_header, sizeof(mhap_header))
+                    < (ssize_t)sizeof(mhap_header)) {
+                return ERROR_IO;
+            }
+
+            // mhap_compatible_sets_size = numCompatibleSets * sizeof(uint8_t)
+            const uint32_t mhap_compatible_sets_size = mhap_header[8];
+            if (chunk_size != sizeof(mhap_header) + mhap_compatible_sets_size) {
+                return ERROR_MALFORMED;
+            }
+
+            data_offset += sizeof(mhap_header);
+            uint8_t mhap_compatible_sets[mhap_compatible_sets_size];
+            if (mDataSource->readAt(
+                    data_offset, mhap_compatible_sets, sizeof(mhap_compatible_sets))
+                            < (ssize_t)sizeof(mhap_compatible_sets)) {
+                return ERROR_IO;
+            }
+
+            AMediaFormat_setBuffer(mLastTrack->meta,
+                    AMEDIAFORMAT_KEY_MPEGH_COMPATIBLE_SETS,
+                    mhap_compatible_sets, sizeof(mhap_compatible_sets));
+            data_offset += sizeof(mhap_compatible_sets);
+            *offset = data_offset;
+            break;
+        }
         case FOURCC("mp4v"):
         case FOURCC("encv"):
         case FOURCC("s263"):
@@ -5001,6 +5105,8 @@
     bool success = AMediaFormat_getString(mFormat, AMEDIAFORMAT_KEY_MIME, &mime);
     CHECK(success);
 
+    mIsMpegH = !strcasecmp(mime, MEDIA_MIMETYPE_AUDIO_MPEGH_MHA1) ||
+               !strcasecmp(mime, MEDIA_MIMETYPE_AUDIO_MPEGH_MHM1);
     mIsAVC = !strcasecmp(mime, MEDIA_MIMETYPE_VIDEO_AVC);
     mIsHEVC = !strcasecmp(mime, MEDIA_MIMETYPE_VIDEO_HEVC) ||
               !strcasecmp(mime, MEDIA_MIMETYPE_IMAGE_ANDROID_HEIC);
@@ -6072,10 +6178,11 @@
             }
 
             uint32_t syncSampleIndex = sampleIndex;
-            // assume every non-USAC audio sample is a sync sample. This works around
+            // assume every non-USAC/non-MPEGH audio sample is a sync sample.
+            // This works around
             // seek issues with files that were incorrectly written with an
             // empty or single-sample stss block for the audio track
-            if (err == OK && (!mIsAudio || mIsUsac)) {
+            if (err == OK && (!mIsAudio || mIsUsac || mIsMpegH)) {
                 err = mSampleTable->findSyncSampleNear(
                         sampleIndex, &syncSampleIndex, findFlags);
             }
diff --git a/media/libmedia/Android.bp b/media/libmedia/Android.bp
index b2056ad..e471c7b 100644
--- a/media/libmedia/Android.bp
+++ b/media/libmedia/Android.bp
@@ -45,6 +45,12 @@
             enabled: false,
         },
     },
+    header_libs: [
+        "libbinder_headers",
+    ],
+    export_header_lib_headers: [
+        "libbinder_headers",
+    ],
     apex_available: [
         "//apex_available:platform",
         "com.android.media",
diff --git a/media/libmediatranscoding/transcoder/benchmark/MediaTranscoderBenchmark.cpp b/media/libmediatranscoding/transcoder/benchmark/MediaTranscoderBenchmark.cpp
index ac3b2c0..8f8ad4e 100644
--- a/media/libmediatranscoding/transcoder/benchmark/MediaTranscoderBenchmark.cpp
+++ b/media/libmediatranscoding/transcoder/benchmark/MediaTranscoderBenchmark.cpp
@@ -113,6 +113,16 @@
     // Asset directory
     static const std::string kAssetDirectory = "/data/local/tmp/TranscodingBenchmark/";
 
+    // Transcoding configuration params to be logged
+    int64_t trackDurationUs = 0;
+    int32_t width = 0;
+    int32_t height = 0;
+    std::string sourceMime = "NA";
+    std::string targetMime = "NA";
+    bool includeAudio = false;
+    bool transcodeVideo = false;
+    int32_t targetBitrate = 0;
+
     int srcFd = 0;
     int dstFd = 0;
 
@@ -163,10 +173,30 @@
                     state.counters[PARAM_VIDEO_FRAME_RATE] = benchmark::Counter(
                             frameCount, benchmark::Counter::kIsIterationInvariantRate);
                 }
+                if (!AMediaFormat_getInt32(srcFormat, AMEDIAFORMAT_KEY_WIDTH, &width)) {
+                    state.SkipWithError("Video source track format does not have width");
+                    goto exit;
+                }
+                if (!AMediaFormat_getInt32(srcFormat, AMEDIAFORMAT_KEY_HEIGHT, &height)) {
+                    state.SkipWithError("Video source track format does not have height");
+                    goto exit;
+                }
+                AMediaFormat_getInt64(srcFormat, AMEDIAFORMAT_KEY_DURATION, &trackDurationUs);
+                sourceMime = mime;
             }
 
             if (trackSelectionCallback(mime, &dstFormat)) {
                 status = transcoder->configureTrackFormat(i, dstFormat);
+                if (strncmp(mime, "video/", 6) == 0 && dstFormat != nullptr) {
+                    const char* mime = nullptr;
+                    if (AMediaFormat_getString(dstFormat, AMEDIAFORMAT_KEY_MIME, &mime)) {
+                        targetMime = mime;
+                    }
+                    AMediaFormat_getInt32(dstFormat, AMEDIAFORMAT_KEY_BIT_RATE, &targetBitrate);
+                    transcodeVideo = true;
+                } else if (strncmp(mime, "audio/", 6) == 0) {
+                    includeAudio = true;
+                }
             }
 
             if (dstFormat != nullptr) {
@@ -195,6 +225,17 @@
         }
     }
 
+    // Set transcoding configuration params in benchmark label
+    state.SetLabel(srcFileName + "," +
+                   std::to_string(width) + "x" + std::to_string(height) + "," +
+                   sourceMime + "," +
+                   std::to_string(trackDurationUs/1000) + "," +
+                   (includeAudio ? "Yes" : "No") + "," +
+                   (transcodeVideo ? "Yes" : "No") + "," +
+                   targetMime + "," +
+                   std::to_string(targetBitrate)
+                   );
+
 exit:
     if (srcFd > 0) close(srcFd);
     if (dstFd > 0) close(dstFd);
@@ -543,7 +584,11 @@
     void PrintRunData(const Run& report);
 
     bool mPrintedHeader;
-    std::vector<std::string> mHeaders = {"name", "real_time", "cpu_time", PARAM_VIDEO_FRAME_RATE};
+    std::vector<std::string> mHeaders = {
+        "File",          "Resolution",     "SourceMime", "VideoTrackDuration(ms)",
+        "IncludeAudio",  "TranscodeVideo", "TargetMime", "TargetBirate(bps)",
+        "real_time(ms)", "cpu_time(ms)",   PARAM_VIDEO_FRAME_RATE
+    };
 };
 
 bool CustomCsvReporter::ReportContext(const Context& context __unused) {
@@ -574,7 +619,8 @@
         return;
     }
     std::ostream& Out = GetOutputStream();
-    Out << run.benchmark_name() << ",";
+    // Log the transcoding params reported through label
+    Out << run.report_label << ",";
     Out << run.GetAdjustedRealTime() << ",";
     Out << run.GetAdjustedCPUTime() << ",";
     auto frameRate = run.counters.find(PARAM_VIDEO_FRAME_RATE);
diff --git a/media/libstagefright/MediaCodec.cpp b/media/libstagefright/MediaCodec.cpp
index 944e007..6b2e7be 100644
--- a/media/libstagefright/MediaCodec.cpp
+++ b/media/libstagefright/MediaCodec.cpp
@@ -102,6 +102,17 @@
 static const char *kCodecWidth = "android.media.mediacodec.width";     /* 0..n */
 static const char *kCodecHeight = "android.media.mediacodec.height";   /* 0..n */
 static const char *kCodecRotation = "android.media.mediacodec.rotation-degrees";  /* 0/90/180/270 */
+static const char *kCodecColorFormat = "android.media.mediacodec.color-format";
+static const char *kCodecFrameRate = "android.media.mediacodec.frame-rate";
+static const char *kCodecCaptureRate = "android.media.mediacodec.capture-rate";
+static const char *kCodecOperatingRate = "android.media.mediacodec.operating-rate";
+static const char *kCodecPriority = "android.media.mediacodec.priority";
+static const char *kCodecRequestedVideoQPIMin = "android.media.mediacodec.video-qp-i-min";
+static const char *kCodecRequestedVideoQPIMax = "android.media.mediacodec.video-qp-i-max";
+static const char *kCodecRequestedVideoQPPMin = "android.media.mediacodec.video-qp-p-min";
+static const char *kCodecRequestedVideoQPPMax = "android.media.mediacodec.video-qp-p-max";
+static const char *kCodecRequestedVideoQPBMin = "android.media.mediacodec.video-qp-b-min";
+static const char *kCodecRequestedVideoQPBMax = "android.media.mediacodec.video-qp-b-max";
 
 // NB: These are not yet exposed as public Java API constants.
 static const char *kCodecCrypto = "android.media.mediacodec.crypto";   /* 0,1 */
@@ -131,6 +142,8 @@
 static const char *kCodecSampleRate = "android.media.mediacodec.sampleRate";
 static const char *kCodecVideoEncodedBytes = "android.media.mediacodec.vencode.bytes";
 static const char *kCodecVideoEncodedFrames = "android.media.mediacodec.vencode.frames";
+static const char *kCodecVideoInputBytes = "android.media.mediacodec.video.input.bytes";
+static const char *kCodecVideoInputFrames = "android.media.mediacodec.video.input.frames";
 static const char *kCodecVideoEncodedDurationUs = "android.media.mediacodec.vencode.durationUs";
 
 // the kCodecRecent* fields appear only in getMetrics() results
@@ -841,6 +854,8 @@
         }
         mediametrics_setInt64(mMetricsHandle, kCodecVideoEncodedDurationUs, duration);
         mediametrics_setInt64(mMetricsHandle, kCodecVideoEncodedFrames, mFramesEncoded);
+        mediametrics_setInt64(mMetricsHandle, kCodecVideoInputFrames, mFramesInput);
+        mediametrics_setInt64(mMetricsHandle, kCodecVideoInputBytes, mBytesInput);
     }
 
     {
@@ -1053,7 +1068,7 @@
 }
 
 // when we send a buffer to the codec;
-void MediaCodec::statsBufferSent(int64_t presentationUs) {
+void MediaCodec::statsBufferSent(int64_t presentationUs, const sp<MediaCodecBuffer> &buffer) {
 
     // only enqueue if we have a legitimate time
     if (presentationUs <= 0) {
@@ -1067,6 +1082,11 @@
         });
     }
 
+    if (mIsVideo && (mFlags & kFlagIsEncoder)) {
+        mBytesInput += buffer->size();
+        mFramesInput++;
+    }
+
     const int64_t nowNs = systemTime(SYSTEM_TIME_MONOTONIC);
     BufferFlightTiming_t startdata = { presentationUs, nowNs };
 
@@ -1450,6 +1470,50 @@
             if (format->findInt32("max-height", &maxHeight)) {
                 mediametrics_setInt32(mMetricsHandle, kCodecMaxHeight, maxHeight);
             }
+            int32_t colorFormat = -1;
+            if (format->findInt32("color-format", &colorFormat)) {
+                mediametrics_setInt32(mMetricsHandle, kCodecColorFormat, colorFormat);
+            }
+            float frameRate = -1.0;
+            if (format->findFloat("frame-rate", &frameRate)) {
+                mediametrics_setDouble(mMetricsHandle, kCodecFrameRate, frameRate);
+            }
+            float captureRate = -1.0;
+            if (format->findFloat("capture-rate", &captureRate)) {
+                mediametrics_setDouble(mMetricsHandle, kCodecCaptureRate, captureRate);
+            }
+            float operatingRate = -1.0;
+            if (format->findFloat("operating-rate", &operatingRate)) {
+                mediametrics_setDouble(mMetricsHandle, kCodecOperatingRate, operatingRate);
+            }
+            int32_t priority = -1;
+            if (format->findInt32("priority", &priority)) {
+                mediametrics_setInt32(mMetricsHandle, kCodecPriority, priority);
+            }
+            int32_t qpIMin = -1;
+            if (format->findInt32("video-qp-i-min", &qpIMin)) {
+                mediametrics_setInt32(mMetricsHandle, kCodecRequestedVideoQPIMin, qpIMin);
+            }
+            int32_t qpIMax = -1;
+            if (format->findInt32("video-qp-i-max", &qpIMax)) {
+                mediametrics_setInt32(mMetricsHandle, kCodecRequestedVideoQPIMax, qpIMax);
+            }
+            int32_t qpPMin = -1;
+            if (format->findInt32("video-qp-p-min", &qpPMin)) {
+                mediametrics_setInt32(mMetricsHandle, kCodecRequestedVideoQPPMin, qpPMin);
+            }
+            int32_t qpPMax = -1;
+            if (format->findInt32("video-qp-p-max", &qpPMax)) {
+                mediametrics_setInt32(mMetricsHandle, kCodecRequestedVideoQPPMax, qpPMax);
+            }
+             int32_t qpBMin = -1;
+            if (format->findInt32("video-qp-b-min", &qpBMin)) {
+                mediametrics_setInt32(mMetricsHandle, kCodecRequestedVideoQPBMin, qpBMin);
+            }
+            int32_t qpBMax = -1;
+            if (format->findInt32("video-qp-b-max", &qpBMax)) {
+                mediametrics_setInt32(mMetricsHandle, kCodecRequestedVideoQPBMax, qpBMax);
+            }
         }
 
         // Prevent possible integer overflow in downstream code.
@@ -4664,7 +4728,7 @@
         info->mOwnedByClient = false;
         info->mData.clear();
 
-        statsBufferSent(timeUs);
+        statsBufferSent(timeUs, buffer);
     }
 
     return err;
diff --git a/media/libstagefright/Utils.cpp b/media/libstagefright/Utils.cpp
index fda9f4c..4c18f87 100644
--- a/media/libstagefright/Utils.cpp
+++ b/media/libstagefright/Utils.cpp
@@ -47,6 +47,16 @@
 #include <media/AudioParameter.h>
 #include <system/audio.h>
 
+// TODO : Remove the defines once mainline media is built against NDK >= 31.
+// The mp4 extractor is part of mainline and builds against NDK 29 as of
+// writing. These keys are available only from NDK 31:
+#define AMEDIAFORMAT_KEY_MPEGH_PROFILE_LEVEL_INDICATION \
+  "mpegh-profile-level-indication"
+#define AMEDIAFORMAT_KEY_MPEGH_REFERENCE_CHANNEL_LAYOUT \
+  "mpegh-reference-channel-layout"
+#define AMEDIAFORMAT_KEY_MPEGH_COMPATIBLE_SETS \
+  "mpegh-compatible-sets"
+
 namespace android {
 
 static status_t copyNALUToABuffer(sp<ABuffer> *buffer, const uint8_t *ptr, size_t length) {
@@ -1085,6 +1095,25 @@
             msg->setInt32("is-adts", isADTS);
         }
 
+        int32_t mpeghProfileLevelIndication;
+        if (meta->findInt32(kKeyMpeghProfileLevelIndication, &mpeghProfileLevelIndication)) {
+            msg->setInt32(AMEDIAFORMAT_KEY_MPEGH_PROFILE_LEVEL_INDICATION,
+                    mpeghProfileLevelIndication);
+        }
+        int32_t mpeghReferenceChannelLayout;
+        if (meta->findInt32(kKeyMpeghReferenceChannelLayout, &mpeghReferenceChannelLayout)) {
+            msg->setInt32(AMEDIAFORMAT_KEY_MPEGH_REFERENCE_CHANNEL_LAYOUT,
+                    mpeghReferenceChannelLayout);
+        }
+        if (meta->findData(kKeyMpeghCompatibleSets, &type, &data, &size)) {
+            sp<ABuffer> buffer = new (std::nothrow) ABuffer(size);
+            if (buffer.get() == NULL || buffer->base() == NULL) {
+                return NO_MEMORY;
+            }
+            msg->setBuffer(AMEDIAFORMAT_KEY_MPEGH_COMPATIBLE_SETS, buffer);
+            memcpy(buffer->data(), data, size);
+        }
+
         int32_t aacProfile = -1;
         if (meta->findInt32(kKeyAACAOT, &aacProfile)) {
             msg->setInt32("aac-profile", aacProfile);
@@ -1850,6 +1879,23 @@
             meta->setInt32(kKeyIsADTS, isADTS);
         }
 
+        int32_t mpeghProfileLevelIndication = -1;
+        if (msg->findInt32(AMEDIAFORMAT_KEY_MPEGH_PROFILE_LEVEL_INDICATION,
+                &mpeghProfileLevelIndication)) {
+            meta->setInt32(kKeyMpeghProfileLevelIndication, mpeghProfileLevelIndication);
+        }
+        int32_t mpeghReferenceChannelLayout = -1;
+        if (msg->findInt32(AMEDIAFORMAT_KEY_MPEGH_REFERENCE_CHANNEL_LAYOUT,
+                &mpeghReferenceChannelLayout)) {
+            meta->setInt32(kKeyMpeghReferenceChannelLayout, mpeghReferenceChannelLayout);
+        }
+        sp<ABuffer> mpeghCompatibleSets;
+        if (msg->findBuffer(AMEDIAFORMAT_KEY_MPEGH_COMPATIBLE_SETS,
+                &mpeghCompatibleSets)) {
+            meta->setData(kKeyMpeghCompatibleSets, kTypeHCOS,
+                    mpeghCompatibleSets->data(), mpeghCompatibleSets->size());
+        }
+
         int32_t aacProfile = -1;
         if (msg->findInt32("aac-profile", &aacProfile)) {
             meta->setInt32(kKeyAACAOT, aacProfile);
diff --git a/media/libstagefright/foundation/include/media/stagefright/foundation/MediaDefs.h b/media/libstagefright/foundation/include/media/stagefright/foundation/MediaDefs.h
index 7644a9a..f5cecef 100644
--- a/media/libstagefright/foundation/include/media/stagefright/foundation/MediaDefs.h
+++ b/media/libstagefright/foundation/include/media/stagefright/foundation/MediaDefs.h
@@ -92,6 +92,8 @@
     kAudioEncodingPcm16bit = 2,
     kAudioEncodingPcm8bit = 3,
     kAudioEncodingPcmFloat = 4,
+    kAudioEncodingPcm24bitPacked = 21,
+    kAudioEncodingPcm32bit = 22,
 };
 
 }  // namespace android
diff --git a/media/libstagefright/id3/test/AndroidTest.xml b/media/libstagefright/id3/test/AndroidTest.xml
index d6ea470..50f9253 100644
--- a/media/libstagefright/id3/test/AndroidTest.xml
+++ b/media/libstagefright/id3/test/AndroidTest.xml
@@ -19,7 +19,7 @@
         <option name="cleanup" value="true" />
         <option name="push" value="ID3Test->/data/local/tmp/ID3Test" />
         <option name="push-file"
-            key="https://storage.googleapis.com/android_media/frameworks/av/media/libstagefright/id3/test/ID3Test-1.1.zip?unzip=true"
+            key="https://storage.googleapis.com/android_media/frameworks/av/media/libstagefright/id3/test/ID3Test-1.2.zip?unzip=true"
             value="/data/local/tmp/ID3TestRes/" />
     </target_preparer>
 
diff --git a/media/libstagefright/id3/test/ID3Test.cpp b/media/libstagefright/id3/test/ID3Test.cpp
index 8db83cb..1ceeb6a 100644
--- a/media/libstagefright/id3/test/ID3Test.cpp
+++ b/media/libstagefright/id3/test/ID3Test.cpp
@@ -135,6 +135,7 @@
     } else {
         ASSERT_EQ(data, nullptr) << "Found album art when expected none!";
     }
+
 #if (LOG_NDEBUG == 0)
     hexdump(data, dataSize > 128 ? 128 : dataSize);
 #endif
@@ -186,7 +187,8 @@
                                            "bbb_1sec_v23_3tags.mp3",
                                            "bbb_1sec_v1_5tags.mp3",
                                            "bbb_2sec_v24_unsynchronizedOneFrame.mp3",
-                                           "bbb_2sec_v24_unsynchronizedAllFrames.mp3"));
+                                           "bbb_2sec_v24_unsynchronizedAllFrames.mp3",
+                                           "idv24_unsynchronized.mp3"));
 
 INSTANTIATE_TEST_SUITE_P(
         id3TestAll, ID3versionTest,
@@ -201,7 +203,8 @@
                           make_pair("bbb_1sec_v1_5tags.mp3", ID3::ID3_V1_1),
                           make_pair("bbb_1sec_v1_3tags.mp3", ID3::ID3_V1_1),
                           make_pair("bbb_2sec_v24_unsynchronizedOneFrame.mp3", ID3::ID3_V2_4),
-                          make_pair("bbb_2sec_v24_unsynchronizedAllFrames.mp3", ID3::ID3_V2_4)));
+                          make_pair("bbb_2sec_v24_unsynchronizedAllFrames.mp3", ID3::ID3_V2_4),
+                          make_pair("idv24_unsynchronized.mp3", ID3::ID3_V2_4)));
 
 INSTANTIATE_TEST_SUITE_P(
         id3TestAll, ID3textTagTest,
@@ -227,7 +230,9 @@
                                            make_pair("bbb_2sec_1_image.mp3", true),
                                            make_pair("bbb_2sec_2_image.mp3", true),
                                            make_pair("bbb_2sec_largeSize.mp3", true),
-                                           make_pair("bbb_1sec_v1_5tags.mp3", false)));
+                                           make_pair("bbb_1sec_v1_5tags.mp3", false),
+                                           make_pair("idv24_unsynchronized.mp3", true)
+                                           ));
 
 INSTANTIATE_TEST_SUITE_P(id3TestAll, ID3multiAlbumArtTest,
                          ::testing::Values(make_pair("bbb_1sec_v23.mp3", 0),
diff --git a/media/libstagefright/include/media/stagefright/MediaCodec.h b/media/libstagefright/include/media/stagefright/MediaCodec.h
index 3f93e6d..0584054 100644
--- a/media/libstagefright/include/media/stagefright/MediaCodec.h
+++ b/media/libstagefright/include/media/stagefright/MediaCodec.h
@@ -572,8 +572,9 @@
     int64_t mBytesEncoded = 0;
     int64_t mEarliestEncodedPtsUs = INT64_MAX;
     int64_t mLatestEncodedPtsUs = INT64_MIN;
-    int32_t mFramesEncoded = 0;
-
+    int64_t mFramesEncoded = 0;
+    int64_t mBytesInput = 0;
+    int64_t mFramesInput = 0;
 
     int64_t mNumLowLatencyEnables;  // how many times low latency mode is enabled
     int64_t mNumLowLatencyDisables;  // how many times low latency mode is disabled
@@ -590,7 +591,7 @@
 
     sp<BatteryChecker> mBatteryChecker;
 
-    void statsBufferSent(int64_t presentationUs);
+    void statsBufferSent(int64_t presentationUs, const sp<MediaCodecBuffer> &buffer);
     void statsBufferReceived(int64_t presentationUs, const sp<MediaCodecBuffer> &buffer);
 
     enum {
diff --git a/media/libstagefright/include/media/stagefright/MetaDataBase.h b/media/libstagefright/include/media/stagefright/MetaDataBase.h
index 08925b5..c80012e 100644
--- a/media/libstagefright/include/media/stagefright/MetaDataBase.h
+++ b/media/libstagefright/include/media/stagefright/MetaDataBase.h
@@ -155,6 +155,10 @@
     kKeyIsADTS            = 'adts',  // bool (int32_t)
     kKeyAACAOT            = 'aaot',  // int32_t
 
+    kKeyMpeghProfileLevelIndication = 'hpli', // int32_t
+    kKeyMpeghReferenceChannelLayout = 'hrcl', // int32_t
+    kKeyMpeghCompatibleSets         = 'hcos', // raw data
+
     // If a MediaBuffer's data represents (at least partially) encrypted
     // data, the following fields aid in decryption.
     // The data can be thought of as pairs of plain and encrypted data
@@ -280,6 +284,7 @@
     kTypeAV1C        = 'av1c',
     kTypeDVCC        = 'dvcc',
     kTypeD263        = 'd263',
+    kTypeHCOS        = 'hcos',
 };
 
 enum {
diff --git a/media/mediaserver/mediaserver.rc b/media/mediaserver/mediaserver.rc
index c82e532..05373c9 100644
--- a/media/mediaserver/mediaserver.rc
+++ b/media/mediaserver/mediaserver.rc
@@ -7,9 +7,3 @@
     group audio camera inet net_bt net_bt_admin net_bw_acct drmrpc mediadrm
     ioprio rt 4
     task_profiles ProcessCapacityHigh HighPerformance
-
-# media.transcoding service is defined on com.android.media apex which goes back
-# to API29, but we only want it started on API31+ devices. So we declare it as
-# "disabled" and start it explicitly on boot.
-on boot
-    start media.transcoding
diff --git a/media/ndk/Android.bp b/media/ndk/Android.bp
index 3007574..8d527e9 100644
--- a/media/ndk/Android.bp
+++ b/media/ndk/Android.bp
@@ -85,7 +85,9 @@
 
 cc_library_shared {
     name: "libmediandk",
-    llndk_stubs: "libmediandk.llndk",
+    llndk: {
+        symbol_file: "libmediandk.map.txt",
+    },
 
     srcs: [
         "NdkJavaVMHelper.cpp",
@@ -168,14 +170,6 @@
     },
 }
 
-llndk_library {
-    name: "libmediandk.llndk",
-    symbol_file: "libmediandk.map.txt",
-    export_include_dirs: [
-        "include",
-    ],
-}
-
 cc_library {
     name: "libmediandk_utils",
 
diff --git a/media/utils/ServiceUtilities.cpp b/media/utils/ServiceUtilities.cpp
index e5eeba3..c19fe38 100644
--- a/media/utils/ServiceUtilities.cpp
+++ b/media/utils/ServiceUtilities.cpp
@@ -211,10 +211,7 @@
 
     if (ok) {
         static const String16 sCaptureHotwordAllowed("android.permission.CAPTURE_AUDIO_HOTWORD");
-        //TODO: b/185972521: see if permission cache can be used with shell identity for CTS
-        PermissionController permissionController;
-        ok = permissionController.checkPermission(sCaptureHotwordAllowed,
-            pid, uid);
+        ok = PermissionCache::checkPermission(sCaptureHotwordAllowed, pid, uid);
     }
     if (!ok) ALOGV("android.permission.CAPTURE_AUDIO_HOTWORD");
     return ok;
@@ -300,6 +297,10 @@
   return identity;
 }
 
+void purgePermissionCache() {
+    PermissionCache::purgeCache();
+}
+
 status_t checkIMemory(const sp<IMemory>& iMemory)
 {
     if (iMemory == 0) {
diff --git a/media/utils/include/mediautils/ServiceUtilities.h b/media/utils/include/mediautils/ServiceUtilities.h
index 9a3c6fb..6e75746 100644
--- a/media/utils/include/mediautils/ServiceUtilities.h
+++ b/media/utils/include/mediautils/ServiceUtilities.h
@@ -97,6 +97,7 @@
 bool dumpAllowed();
 bool modifyPhoneStateAllowed(const media::permission::Identity& identity);
 bool bypassInterruptionPolicyAllowed(const media::permission::Identity& identity);
+void purgePermissionCache();
 
 media::permission::Identity getCallingIdentity();
 
diff --git a/services/audiopolicy/service/AudioPolicyService.cpp b/services/audiopolicy/service/AudioPolicyService.cpp
index b5eb98f..fb38e3d 100644
--- a/services/audiopolicy/service/AudioPolicyService.cpp
+++ b/services/audiopolicy/service/AudioPolicyService.cpp
@@ -1019,6 +1019,9 @@
         return handleResetUidState(args, err);
     } else if (args.size() >= 2 && args[0] == String16("get-uid-state")) {
         return handleGetUidState(args, out, err);
+    } else if (args.size() >= 1 && args[0] == String16("purge_permission-cache")) {
+        purgePermissionCache();
+        return NO_ERROR;
     } else if (args.size() == 1 && args[0] == String16("help")) {
         printHelp(out);
         return NO_ERROR;
diff --git a/services/camera/libcameraservice/CameraService.cpp b/services/camera/libcameraservice/CameraService.cpp
index 65f27b1..3e6a7c7 100644
--- a/services/camera/libcameraservice/CameraService.cpp
+++ b/services/camera/libcameraservice/CameraService.cpp
@@ -91,6 +91,8 @@
 using hardware::ICameraServiceListener;
 using hardware::camera::common::V1_0::CameraDeviceStatus;
 using hardware::camera::common::V1_0::TorchModeStatus;
+using hardware::camera2::ICameraInjectionCallback;
+using hardware::camera2::ICameraInjectionSession;
 using hardware::camera2::utils::CameraIdAndSessionConfiguration;
 using hardware::camera2::utils::ConcurrentCameraIdCombination;
 
@@ -127,6 +129,8 @@
         sCameraSendSystemEventsPermission("android.permission.CAMERA_SEND_SYSTEM_EVENTS");
 static const String16 sCameraOpenCloseListenerPermission(
         "android.permission.CAMERA_OPEN_CLOSE_LISTENER");
+static const String16
+        sCameraInjectExternalCameraPermission("android.permission.CAMERA_INJECT_EXTERNAL_CAMERA");
 
 static constexpr int32_t kVendorClientScore = resource_policy::PERCEPTIBLE_APP_ADJ;
 static constexpr int32_t kVendorClientState = ActivityManager::PROCESS_STATE_PERSISTENT_UI;
@@ -166,6 +170,7 @@
     mUidPolicy->registerSelf();
     mSensorPrivacyPolicy = new SensorPrivacyPolicy(this);
     mSensorPrivacyPolicy->registerSelf();
+    mInjectionStatusListener = new InjectionStatusListener(this);
     mAppOps.setCameraAudioRestriction(mAudioRestriction);
     sp<HidlCameraService> hcs = HidlCameraService::getInstance(this);
     if (hcs->registerAsService() != android::OK) {
@@ -243,6 +248,7 @@
     VendorTagDescriptor::clearGlobalVendorTagDescriptor();
     mUidPolicy->unregisterSelf();
     mSensorPrivacyPolicy->unregisterSelf();
+    mInjectionStatusListener->removeListener();
 }
 
 void CameraService::onNewProviderRegistered() {
@@ -2386,6 +2392,42 @@
     return Status::ok();
 }
 
+Status CameraService::injectCamera(
+        const String16& packageName, const String16& internalCamId,
+        const String16& externalCamId,
+        const sp<ICameraInjectionCallback>& callback,
+        /*out*/
+        sp<hardware::camera2::ICameraInjectionSession>* cameraInjectionSession) {
+    ATRACE_CALL();
+
+    if (!checkCallingPermission(sCameraInjectExternalCameraPermission)) {
+        const int pid = CameraThreadState::getCallingPid();
+        const int uid = CameraThreadState::getCallingUid();
+        ALOGE("Permission Denial: can't inject camera pid=%d, uid=%d", pid, uid);
+        return STATUS_ERROR(ERROR_PERMISSION_DENIED,
+                        "Permission Denial: no permission to inject camera");
+    }
+
+    ALOGV(
+        "%s: Package name = %s, Internal camera ID = %s, External camera ID = "
+        "%s",
+        __FUNCTION__, String8(packageName).string(),
+        String8(internalCamId).string(), String8(externalCamId).string());
+
+    binder::Status ret = binder::Status::ok();
+    // TODO: Implement the injection camera function.
+    // ret = internalInjectCamera(...);
+    // if(!ret.isOk()) {
+    //     mInjectionStatusListener->notifyInjectionError(...);
+    //     return ret;
+    // }
+
+    mInjectionStatusListener->addListener(callback);
+    *cameraInjectionSession = new CameraInjectionSession(this);
+
+    return ret;
+}
+
 void CameraService::removeByClient(const BasicClient* client) {
     Mutex::Autolock lock(mServiceLock);
     for (auto& i : mActiveClientManager.getAll()) {
@@ -3614,6 +3656,66 @@
 }
 
 // ----------------------------------------------------------------------------
+//                  InjectionStatusListener
+// ----------------------------------------------------------------------------
+
+void CameraService::InjectionStatusListener::addListener(
+        const sp<ICameraInjectionCallback>& callback) {
+    Mutex::Autolock lock(mListenerLock);
+    if (mCameraInjectionCallback) return;
+    status_t res = IInterface::asBinder(callback)->linkToDeath(this);
+    if (res == OK) {
+        mCameraInjectionCallback = callback;
+    }
+}
+
+void CameraService::InjectionStatusListener::removeListener() {
+    Mutex::Autolock lock(mListenerLock);
+    if (mCameraInjectionCallback == nullptr) {
+        ALOGW("InjectionStatusListener: mCameraInjectionCallback == nullptr");
+        return;
+    }
+    IInterface::asBinder(mCameraInjectionCallback)->unlinkToDeath(this);
+    mCameraInjectionCallback = nullptr;
+}
+
+void CameraService::InjectionStatusListener::notifyInjectionError(
+        int errorCode) {
+    Mutex::Autolock lock(mListenerLock);
+    if (mCameraInjectionCallback == nullptr) {
+        ALOGW("InjectionStatusListener: mCameraInjectionCallback == nullptr");
+        return;
+    }
+    mCameraInjectionCallback->onInjectionError(errorCode);
+}
+
+void CameraService::InjectionStatusListener::binderDied(
+        const wp<IBinder>& /*who*/) {
+    Mutex::Autolock lock(mListenerLock);
+    ALOGV("InjectionStatusListener: ICameraInjectionCallback has died");
+    auto parent = mParent.promote();
+    if (parent != nullptr) {
+        parent->stopInjectionImpl();
+    }
+}
+
+// ----------------------------------------------------------------------------
+//                  CameraInjectionSession
+// ----------------------------------------------------------------------------
+
+binder::Status CameraService::CameraInjectionSession::stopInjection() {
+    Mutex::Autolock lock(mInjectionSessionLock);
+    auto parent = mParent.promote();
+    if (parent == nullptr) {
+        ALOGE("CameraInjectionSession: Parent is gone");
+        return STATUS_ERROR(ICameraInjectionCallback::ERROR_INJECTION_SERVICE,
+                "Camera service encountered error");
+    }
+    parent->stopInjectionImpl();
+    return binder::Status::ok();
+}
+
+// ----------------------------------------------------------------------------
 
 static const int kDumpLockRetries = 50;
 static const int kDumpLockSleep = 60000;
@@ -4273,4 +4375,10 @@
     return mode;
 }
 
+void CameraService::stopInjectionImpl() {
+    mInjectionStatusListener->removeListener();
+
+    // TODO: Implement the stop injection function.
+}
+
 }; // namespace android
diff --git a/services/camera/libcameraservice/CameraService.h b/services/camera/libcameraservice/CameraService.h
index b436cec..10e1748 100644
--- a/services/camera/libcameraservice/CameraService.h
+++ b/services/camera/libcameraservice/CameraService.h
@@ -20,6 +20,8 @@
 #include <android/hardware/BnCameraService.h>
 #include <android/hardware/BnSensorPrivacyListener.h>
 #include <android/hardware/ICameraServiceListener.h>
+#include <android/hardware/camera2/BnCameraInjectionSession.h>
+#include <android/hardware/camera2/ICameraInjectionCallback.h>
 
 #include <cutils/multiuser.h>
 #include <utils/Vector.h>
@@ -180,6 +182,13 @@
             /*out*/
             bool *isSupported);
 
+    virtual binder::Status injectCamera(
+            const String16& packageName, const String16& internalCamId,
+            const String16& externalCamId,
+            const sp<hardware::camera2::ICameraInjectionCallback>& callback,
+            /*out*/
+            sp<hardware::camera2::ICameraInjectionSession>* cameraInjectionSession);
+
     // Extra permissions checks
     virtual status_t    onTransact(uint32_t code, const Parcel& data,
                                    Parcel* reply, uint32_t flags);
@@ -1147,6 +1156,46 @@
 
     // Current camera mute mode
     bool mOverrideCameraMuteMode = false;
+
+    /**
+     * A listener class that implements the IBinder::DeathRecipient interface
+     * for use to call back the error state injected by the external camera, and
+     * camera service can kill the injection when binder signals process death.
+     */
+    class InjectionStatusListener : public virtual IBinder::DeathRecipient {
+        public:
+            InjectionStatusListener(sp<CameraService> parent) : mParent(parent) {}
+
+            void addListener(const sp<hardware::camera2::ICameraInjectionCallback>& callback);
+            void removeListener();
+            void notifyInjectionError(int errorCode);
+
+            // IBinder::DeathRecipient implementation
+            virtual void binderDied(const wp<IBinder>& who);
+
+        private:
+            Mutex mListenerLock;
+            wp<CameraService> mParent;
+            sp<hardware::camera2::ICameraInjectionCallback> mCameraInjectionCallback;
+    };
+
+    sp<InjectionStatusListener> mInjectionStatusListener;
+
+    /**
+     * A class that implements the hardware::camera2::BnCameraInjectionSession interface
+     */
+    class CameraInjectionSession : public hardware::camera2::BnCameraInjectionSession {
+        public:
+            CameraInjectionSession(sp<CameraService> parent) : mParent(parent) {}
+            virtual ~CameraInjectionSession() {}
+            binder::Status stopInjection() override;
+
+        private:
+            Mutex mInjectionSessionLock;
+            wp<CameraService> mParent;
+    };
+
+    void stopInjectionImpl();
 };
 
 } // namespace android
diff --git a/services/mediametrics/statsd_codec.cpp b/services/mediametrics/statsd_codec.cpp
index 381f441..4539ad5 100644
--- a/services/mediametrics/statsd_codec.cpp
+++ b/services/mediametrics/statsd_codec.cpp
@@ -95,7 +95,7 @@
     if ( item->getInt32("android.media.mediacodec.rotation-degrees", &rotation)) {
         metrics_proto.set_rotation(rotation);
     }
-    // android.media.mediacodec.crypto  int32 (although missing if not needed
+    // android.media.mediacodec.crypto  int32 (although missing if not needed)
     int32_t crypto = -1;
     if ( item->getInt32("android.media.mediacodec.crypto", &crypto)) {
         metrics_proto.set_crypto(crypto);
@@ -185,15 +185,114 @@
         metrics_proto.set_lifetime_millis(lifetime_millis);
     }
 
-    // new for S; need to plumb through to westworld
-    // android.media.mediacodec.channelCount int32
-    // android.media.mediacodec.sampleRate int32
+    // android.media.mediacodec.channelCount
+    int32_t channelCount = -1;
+    if ( item->getInt32("android.media.mediacodec.channelCount", &channelCount)) {
+        metrics_proto.set_channel_count(channelCount);
+    }
 
-    // new for S; need to plumb through to westworld
+    // android.media.mediacodec.sampleRate
+    int32_t sampleRate = -1;
+    if ( item->getInt32("android.media.mediacodec.sampleRate", &sampleRate)) {
+        metrics_proto.set_sample_rate(sampleRate);
+    }
+
     // TODO PWG may want these fuzzed up a bit to obscure some precision
-    // android.media.mediacodec.vencode.bytes int64
-    // android.media.mediacodec.vencode.frames int64
-    // android.media.mediacodec.vencode.durationUs int64
+    // android.media.mediacodec.vencode.bytes
+    int64_t bytes = -1;
+    if ( item->getInt64("android.media.mediacodec.vencode.bytes", &bytes)) {
+        metrics_proto.set_video_encode_bytes(bytes);
+    }
+
+    // android.media.mediacodec.vencode.frames
+    int64_t frames = -1;
+    if ( item->getInt64("android.media.mediacodec.vencode.frames", &frames)) {
+        metrics_proto.set_video_encode_frames(frames);
+    }
+
+    // android.media.mediacodec.vencode.durationUs
+    int64_t durationUs = -1;
+    if ( item->getInt64("android.media.mediacodec.vencode.durationUs", &durationUs)) {
+        metrics_proto.set_video_encode_duration_us(durationUs);
+    }
+
+    // android.media.mediacodec.color-format
+    int32_t colorFormat = -1;
+    if ( item->getInt32("android.media.mediacodec.color-format", &colorFormat)) {
+        metrics_proto.set_color_format(colorFormat);
+    }
+
+    // android.media.mediacodec.frame-rate
+    double frameRate = -1.0;
+    if ( item->getDouble("android.media.mediacodec.frame-rate", &frameRate)) {
+        metrics_proto.set_frame_rate(frameRate);
+    }
+
+    // android.media.mediacodec.capture-rate
+    double captureRate = -1.0;
+    if ( item->getDouble("android.media.mediacodec.capture-rate", &captureRate)) {
+        metrics_proto.set_capture_rate(captureRate);
+    }
+
+    // android.media.mediacodec.operating-rate
+    double operatingRate = -1.0;
+    if ( item->getDouble("android.media.mediacodec.operating-rate", &operatingRate)) {
+        metrics_proto.set_operating_rate(operatingRate);
+    }
+
+    // android.media.mediacodec.priority
+    int32_t priority = -1;
+    if ( item->getInt32("android.media.mediacodec.priority", &priority)) {
+        metrics_proto.set_priority(priority);
+    }
+
+    // android.media.mediacodec.video-qp-i-min
+    int32_t qpIMin = -1;
+    if ( item->getInt32("android.media.mediacodec.video-qp-i-min", &qpIMin)) {
+        metrics_proto.set_video_qp_i_min(qpIMin);
+    }
+
+    // android.media.mediacodec.video-qp-i-max
+    int32_t qpIMax = -1;
+    if ( item->getInt32("android.media.mediacodec.video-qp-i-max", &qpIMax)) {
+        metrics_proto.set_video_qp_i_max(qpIMax);
+    }
+
+    // android.media.mediacodec.video-qp-p-min
+    int32_t qpPMin = -1;
+    if ( item->getInt32("android.media.mediacodec.video-qp-p-min", &qpPMin)) {
+        metrics_proto.set_video_qp_p_min(qpPMin);
+    }
+
+    // android.media.mediacodec.video-qp-p-max
+    int32_t qpPMax = -1;
+    if ( item->getInt32("android.media.mediacodec.video-qp-p-max", &qpPMax)) {
+        metrics_proto.set_video_qp_p_max(qpPMax);
+    }
+
+    // android.media.mediacodec.video-qp-b-min
+    int32_t qpBMin = -1;
+    if ( item->getInt32("android.media.mediacodec.video-qp-b-min", &qpBMin)) {
+        metrics_proto.set_video_qp_b_min(qpIMin);
+    }
+
+    // android.media.mediacodec.video-qp-b-max
+    int32_t qpBMax = -1;
+    if ( item->getInt32("android.media.mediacodec.video-qp-b-max", &qpBMax)) {
+        metrics_proto.set_video_qp_b_max(qpBMax);
+    }
+
+    // android.media.mediacodec.video.input.bytes
+    int64_t inputBytes = -1;
+    if ( item->getInt64("android.media.mediacodec.video.input.bytes", &inputBytes)) {
+        metrics_proto.set_video_input_bytes(inputBytes);
+    }
+
+    // android.media.mediacodec.video.input.frames
+    int64_t inputFrames = -1;
+    if ( item->getInt64("android.media.mediacodec.video.input.frames", &inputFrames)) {
+        metrics_proto.set_video_input_frames(inputFrames);
+    }
 
     std::string serialized;
     if (!metrics_proto.SerializeToString(&serialized)) {
diff --git a/services/mediatranscoding/MediaTranscodingService.cpp b/services/mediatranscoding/MediaTranscodingService.cpp
index 8b64134..b80fe57 100644
--- a/services/mediatranscoding/MediaTranscodingService.cpp
+++ b/services/mediatranscoding/MediaTranscodingService.cpp
@@ -131,10 +131,10 @@
 void MediaTranscodingService::instantiate() {
     std::shared_ptr<MediaTranscodingService> service =
             ::ndk::SharedRefBase::make<MediaTranscodingService>();
-    binder_status_t status =
-            AServiceManager_addService(service->asBinder().get(), getServiceName());
-    if (status != STATUS_OK) {
-        return;
+    if (__builtin_available(android __TRANSCODING_MIN_API__, *)) {
+        // Once service is started, we want it to stay even is client side perished.
+        AServiceManager_forceLazyServicesPersist(true /*persist*/);
+        (void)AServiceManager_registerLazyService(service->asBinder().get(), getServiceName());
     }
 }
 
diff --git a/services/mediatranscoding/tests/MediaTranscodingServiceTestHelper.h b/services/mediatranscoding/tests/MediaTranscodingServiceTestHelper.h
index 20e4bfb..0cb2fad 100644
--- a/services/mediatranscoding/tests/MediaTranscodingServiceTestHelper.h
+++ b/services/mediatranscoding/tests/MediaTranscodingServiceTestHelper.h
@@ -481,7 +481,7 @@
         // Need thread pool to receive callbacks, otherwise oneway callbacks are
         // silently ignored.
         ABinderProcess_startThreadPool();
-        ::ndk::SpAIBinder binder(AServiceManager_getService("media.transcoding"));
+        ::ndk::SpAIBinder binder(AServiceManager_waitForService("media.transcoding"));
         mService = IMediaTranscodingService::fromBinder(binder);
         if (mService == nullptr) {
             ALOGE("Failed to connect to the media.trascoding service.");