Merge changes I02c1e5f2,Ie9292dfd,I232d4a02,I7fcd05f6,Ic3e4750f into emu-master-dev

* changes:
  [Bluetooth] Make nimble examples discoverable by emulator.
  [Bluetooth] Disable failing e2e tests in nimble.
  [Bluetooth] Make the timing related tests runnable.
  [Bluetooth] Fix nimble OS binding bugs.
  [Bluetooth] Enable nimble e2e tests
diff --git a/android-qemu2-glue/main.cpp b/android-qemu2-glue/main.cpp
index 9e90f83..b434a15 100755
--- a/android-qemu2-glue/main.cpp
+++ b/android-qemu2-glue/main.cpp
@@ -23,6 +23,7 @@
 #include "android/base/threads/Async.h"
 #include "android/base/threads/Thread.h"
 #include "android/boot-properties.h"
+#include "android/bootconfig.h"
 #include "android/camera/camera-virtualscene.h"
 #include "android/cmdline-option.h"
 #include "android/config/BluetoothConfig.h"
@@ -2031,6 +2032,8 @@
     // won't appreciate it.
     args.add("-nodefaults");
 
+    std::string bootconfigInitrdPath;
+
     if (hw->hw_arc) {
         args.add2("-kernel", hw->kernel_path);
 
@@ -2047,7 +2050,15 @@
                        avd_dir, avd_dir, avd_dir);
     } else {
         if (hw->disk_ramdisk_path) {
-            args.add({"-kernel", hw->kernel_path, "-initrd", hw->disk_ramdisk_path});
+            args.add2("-kernel", hw->kernel_path);
+
+            if (fc::isEnabled(fc::AndroidbootProps)) {
+                bootconfigInitrdPath =
+                    getWriteableFilename(hw->disk_dataPartition_path, "initrd");
+                args.add2("-initrd", bootconfigInitrdPath.c_str());
+            } else {
+                args.add2("-initrd", hw->disk_ramdisk_path);
+            }
         } else {
             derror("disk_ramdisk_path is required but missing");
             return 1;
@@ -2127,7 +2138,10 @@
                 args.add("null,id=forhvc1");
             }
 
-            boot_property_add_logcat_pipe_virtconsole("*:V");
+            if (!fc::isEnabled(fc::AndroidbootProps)) {
+                // it is going to bootconfig, `androidboot.logcat`
+                boot_property_add_logcat_pipe_virtconsole("*:V");
+            }
         } else {
             args.add("null,id=forhvc1");
         }
@@ -2502,8 +2516,24 @@
                 hw->display_settings_xml);
 
         std::vector<std::string> kernelCmdLineUserspaceBootOpts;
-        for (const auto& kv: userspaceBootOpts) {
-            kernelCmdLineUserspaceBootOpts.push_back(kv.first + "=" + kv.second);
+        if (fc::isEnabled(fc::AndroidbootProps)) {
+            const int r = createRamdiskWithBootconfig(
+                hw->disk_ramdisk_path,
+                bootconfigInitrdPath.c_str(),
+                userspaceBootOpts);
+            if (r) {
+                fprintf(stderr, "%s:%d Could not prepare the ramdisk with bootconfig, "
+                        "error=%d src=%s dst=%s\n", __func__, __LINE__,
+                        r, hw->disk_ramdisk_path, bootconfigInitrdPath.c_str());
+
+                return r;
+            }
+
+            kernelCmdLineUserspaceBootOpts.push_back("bootconfig");
+        } else {
+            for (const auto& kv: userspaceBootOpts) {
+                kernelCmdLineUserspaceBootOpts.push_back(kv.first + "=" + kv.second);
+            }
         }
 
         std::string append_arg = emulator_getKernelParameters(
diff --git a/android/android-emu/android-emu.cmake b/android/android-emu/android-emu.cmake
index ec3deb1..19a8788 100644
--- a/android/android-emu/android-emu.cmake
+++ b/android/android-emu/android-emu.cmake
@@ -23,6 +23,7 @@
     android/base/async/CallbackRegistry.cpp
     android/base/LayoutResolver.cpp
     android/boot-properties.c
+    android/bootconfig.cpp
     android/car-cluster.cpp
     android/car.cpp
     android/cmdline-option.cpp
@@ -818,6 +819,7 @@
       android/base/IOVector_unittest.cpp
       android/base/LayoutResolver_unittest.cpp
       android/base/testing/ProtobufMatchers.cpp
+      android/bootconfig_unittest.cpp
       android/camera/CameraFormatConverters_unittest.cpp
       android/cmdline-option_unittest.cpp
       android/CommonReportedInfo_unittest.cpp
diff --git a/android/android-emu/android/bootconfig.cpp b/android/android-emu/android/bootconfig.cpp
new file mode 100644
index 0000000..48ba137
--- /dev/null
+++ b/android/android-emu/android/bootconfig.cpp
@@ -0,0 +1,126 @@
+#include "android/bootconfig.h"
+
+#include <numeric>
+#include <memory>
+#include <string_view>
+#include <stdio.h>
+
+namespace {
+using namespace std::literals;
+
+constexpr std::string_view kBootconfigMagic = "#BOOTCONFIG\n"sv;
+constexpr uint32_t kBootconfigAlign = 4;
+
+std::pair<int, size_t> copyFile(FILE* src, FILE* dst) {
+    size_t sz = 0;
+    std::vector<char> buf(64 * 1024);
+
+    while (true) {
+        const size_t szR = ::fread(buf.data(), 1, buf.size(), src);
+        if (!szR) {
+            return {::ferror(src), sz};
+        }
+
+        const size_t szW = ::fwrite(buf.data(), 1, szR, dst);
+        if (szR != szW) {
+            return {::ferror(dst), sz};
+        }
+
+        sz += szR;
+    }
+}
+
+void host2le32(const uint32_t v32, void* dst) {
+    auto m8 = static_cast<uint8_t*>(dst);
+    m8[0] = v32;
+    m8[1] = v32 >> 8;
+    m8[2] = v32 >> 16;
+    m8[3] = v32 >> 24;
+}
+
+std::vector<char> flattenBootconfig(const std::vector<std::pair<std::string, std::string>>& bootconfig) {
+    std::vector<char> bits;
+
+    for (const auto& kv: bootconfig) {
+        bits.insert(bits.end(), kv.first.begin(), kv.first.end());
+        bits.push_back('=');
+        bits.push_back('\"');
+        bits.insert(bits.end(), kv.second.begin(), kv.second.end());
+        bits.push_back('\"');
+        bits.push_back('\n');
+    }
+    bits.push_back(0);  // it is ASCIIZ
+
+    return bits;
+}
+
+int appendBootconfig(const size_t srcSize,
+                     const std::vector<std::pair<std::string, std::string>>& bootconfig,
+                     FILE* dst) {
+    const std::vector<char> blob = buildBootconfigBlob(srcSize, bootconfig);
+
+    if (blob.size() != ::fwrite(blob.data(), 1, blob.size(), dst)) {
+        return ::ferror(dst);
+    }
+
+    return 0;
+}
+}  // namespace
+
+std::vector<char> buildBootconfigBlob(const size_t srcSize,
+                                      const std::vector<std::pair<std::string, std::string>>& bootconfig) {
+    std::vector<char> blob = flattenBootconfig(bootconfig);
+
+    const size_t unaligend = (srcSize + blob.size()) % kBootconfigAlign;
+    if (unaligend) {
+        blob.insert(blob.end(), kBootconfigAlign - unaligend, '+');
+    }
+
+    const uint32_t csum =
+        std::accumulate(blob.begin(), blob.end(), 0,
+        [](const uint32_t z, const char c){
+            return z + static_cast<uint8_t>(c);
+        });
+
+    const size_t size = blob.size();
+
+    blob.insert(blob.end(), 8, '+');    // size(u32, LE), csum(u32, LE)
+    host2le32(size, &blob[blob.size() - 8]);
+    host2le32(csum, &blob[blob.size() - 4]);
+
+    blob.insert(blob.end(), kBootconfigMagic.begin(), kBootconfigMagic.end());
+
+    return blob;
+}
+
+int createRamdiskWithBootconfig(const char* srcRamdiskPath,
+                                const char* dstRamdiskPath,
+                                const std::vector<std::pair<std::string, std::string>>& bootconfig) {
+    struct FILE_deleter {
+        void operator()(FILE* fp) const {
+            ::fclose(fp);
+        }
+    };
+
+    std::unique_ptr<FILE, FILE_deleter> srcRamdisk(::fopen(srcRamdiskPath, "rb"));
+    if (!srcRamdisk) {
+        fprintf(stderr, "%s:%d Can't open '%s' for reading\n", __func__, __LINE__, srcRamdiskPath);
+        return 1;
+    }
+
+    std::unique_ptr<FILE, FILE_deleter> dstRamdisk(::fopen(dstRamdiskPath, "wb"));
+    if (!dstRamdisk) {
+        fprintf(stderr, "%s:%d Can't open '%s' for writing\n", __func__, __LINE__, dstRamdiskPath);
+        return 1;
+    }
+
+    const auto r = copyFile(srcRamdisk.get(), dstRamdisk.get());
+    if (r.first) {
+        fprintf(stderr, "%s:%d Error copying '%s' into '%s'\n",
+                __func__, __LINE__, srcRamdiskPath, dstRamdiskPath);
+        return r.first;
+    }
+
+    return appendBootconfig(r.second, bootconfig, dstRamdisk.get());
+}
+
diff --git a/android/android-emu/android/bootconfig.h b/android/android-emu/android/bootconfig.h
new file mode 100644
index 0000000..eee57b9
--- /dev/null
+++ b/android/android-emu/android/bootconfig.h
@@ -0,0 +1,31 @@
+// Copyright 2021 The Android Open Source Project
+//
+// This software is licensed under the terms of the GNU General Public
+// License version 2, as published by the Free Software Foundation, and
+// may be copied, distributed, and modified under those terms.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+
+#pragma once
+
+#include <string>
+#include <vector>
+
+#include "android/utils/compiler.h"
+
+ANDROID_BEGIN_HEADER
+
+// [src ramdisk][bootconfig][padding][size(le32)][csum(le32)][#BOOTCONFIG\n]
+//                                   ^ 4 byte aligned
+
+std::vector<char> buildBootconfigBlob(const size_t srcSize,
+                                      const std::vector<std::pair<std::string, std::string>>& bootconfig);
+
+int createRamdiskWithBootconfig(const char* srcRamdiskPath,
+                                const char* dstRamdiskPath,
+                                const std::vector<std::pair<std::string, std::string>>& bootconfig);
+
+ANDROID_END_HEADER
diff --git a/android/android-emu/android/bootconfig_unittest.cpp b/android/android-emu/android/bootconfig_unittest.cpp
new file mode 100644
index 0000000..56a9b99
--- /dev/null
+++ b/android/android-emu/android/bootconfig_unittest.cpp
@@ -0,0 +1,90 @@
+// Copyright 2021 The Android Open Source Project
+//
+// This software is licensed under the terms of the GNU General Public
+// License version 2, as published by the Free Software Foundation, and
+// may be copied, distributed, and modified under those terms.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+
+#include "android/bootconfig.h"
+
+#include <string_view>
+#include <gtest/gtest.h>
+#include <string.h>
+
+namespace android {
+
+using namespace std::literals;
+
+namespace {
+constexpr std::string_view kBootconfigMagic = "#BOOTCONFIG\n"sv;
+
+uint32_t loadLE32(const void* m) {
+    const uint8_t* m8 = static_cast<const uint8_t*>(m);
+    return uint32_t(m8[0]) | (uint32_t(m8[1]) << 8)
+           | (uint32_t(m8[2]) << 16) | (uint32_t(m8[3]) << 24);
+}
+}  // namespace
+
+TEST(buildBootconfigBlob, OptsMagic) {
+    const std::vector<std::pair<std::string, std::string>> bootconfig = {
+        {"a", "b"},
+        {"c", "2"},
+    };
+
+    constexpr auto propsBlob = "a=\"b\"\nc=\"2\"\n\0"sv;
+
+    const auto blob = buildBootconfigBlob(0, bootconfig);
+
+    EXPECT_GT(blob.size(), propsBlob.size() + kBootconfigMagic.size());
+    EXPECT_TRUE(!memcmp(blob.data(), propsBlob.data(), propsBlob.size()));
+    EXPECT_TRUE(!memcmp(&blob[blob.size() - kBootconfigMagic.size()],
+                        kBootconfigMagic.data(), kBootconfigMagic.size()));
+}
+
+TEST(buildBootconfigBlob, SizeAlignmentCsum) {
+    const std::vector<std::pair<std::string, std::string>> bootconfig = {
+        {"a", "b"},
+    };
+
+    constexpr auto propsBlob = "a=\"b\"\n\0"sv;  // 7 byte long
+    constexpr uint32_t propsCsum = 'a' + '=' + '\"' + 'b' + '\"' + '\n';
+
+    {
+        const auto blob = buildBootconfigBlob(0, bootconfig);
+        EXPECT_EQ(blob.size() - kBootconfigMagic.size() - 8 - propsBlob.size(), 1);
+
+        const char* lencsum = &blob[blob.size() - kBootconfigMagic.size() - 8];
+        EXPECT_EQ(loadLE32(lencsum), propsBlob.size() + 1);
+        EXPECT_EQ(loadLE32(lencsum + 4), propsCsum + '+');
+    }
+    {
+        const auto blob = buildBootconfigBlob(1, bootconfig);
+        EXPECT_EQ(blob.size() - kBootconfigMagic.size() - 8 - propsBlob.size(), 0);
+
+        const char* lencsum = &blob[blob.size() - kBootconfigMagic.size() - 8];
+        EXPECT_EQ(loadLE32(lencsum), propsBlob.size());
+        EXPECT_EQ(loadLE32(lencsum + 4), propsCsum);
+    }
+    {
+        const auto blob = buildBootconfigBlob(2, bootconfig);
+        EXPECT_EQ(blob.size() - kBootconfigMagic.size() - 8 - propsBlob.size(), 3);
+
+        const char* lencsum = &blob[blob.size() - kBootconfigMagic.size() - 8];
+        EXPECT_EQ(loadLE32(lencsum), propsBlob.size() + 3);
+        EXPECT_EQ(loadLE32(lencsum + 4), propsCsum + '+' + '+' + '+');
+    }
+    {
+        const auto blob = buildBootconfigBlob(3, bootconfig);
+        EXPECT_EQ(blob.size() - kBootconfigMagic.size() - 8 - propsBlob.size(), 2);
+
+        const char* lencsum = &blob[blob.size() - kBootconfigMagic.size() - 8];
+        EXPECT_EQ(loadLE32(lencsum), propsBlob.size() + 2);
+        EXPECT_EQ(loadLE32(lencsum + 4), propsCsum + '+' + '+');
+    }
+}
+
+}  // namespace android
diff --git a/android/android-emu/android/emulation/MediaFfmpegVideoHelper.cpp b/android/android-emu/android/emulation/MediaFfmpegVideoHelper.cpp
index 7b369f8..0a7234b 100644
--- a/android/android-emu/android/emulation/MediaFfmpegVideoHelper.cpp
+++ b/android/android-emu/android/emulation/MediaFfmpegVideoHelper.cpp
@@ -185,6 +185,12 @@
     }
 }
 
+int MediaFfmpegVideoHelper::frameReorderBufferSize() const {
+    if (!mCodecCtx) return 0;
+    // the has_bframe: Size of the frame reordering buffer in the decoder.
+    return mCodecCtx->has_b_frames;
+}
+
 void MediaFfmpegVideoHelper::decode(const uint8_t* data,
                                     size_t len,
                                     uint64_t pts) {
diff --git a/android/android-emu/android/emulation/MediaFfmpegVideoHelper.h b/android/android-emu/android/emulation/MediaFfmpegVideoHelper.h
index 1244d64..a253e20 100644
--- a/android/android-emu/android/emulation/MediaFfmpegVideoHelper.h
+++ b/android/android-emu/android/emulation/MediaFfmpegVideoHelper.h
@@ -59,6 +59,9 @@
     void flush() override;
     void deInit() override;
 
+    // this is special helper function, mostly used by apple vtb
+    // to reorder the output frames
+    int frameReorderBufferSize() const;
 private:
     std::vector<uint8_t> mDecodedFrame;
 
diff --git a/android/android-emu/android/emulation/MediaVideoToolBoxVideoHelper.cpp b/android/android-emu/android/emulation/MediaVideoToolBoxVideoHelper.cpp
index f3d4c14..1eb215f 100644
--- a/android/android-emu/android/emulation/MediaVideoToolBoxVideoHelper.cpp
+++ b/android/android-emu/android/emulation/MediaVideoToolBoxVideoHelper.cpp
@@ -67,6 +67,8 @@
 bool MediaVideoToolBoxVideoHelper::init() {
     VTB_DPRINT("init calling");
     constexpr int numthreads = 1;
+    mFfmpegVideoHelper.reset(new MediaFfmpegVideoHelper(264, numthreads));
+    mFfmpegVideoHelper->init();
     mVtbReady = false;
     return true;
 }
@@ -89,16 +91,39 @@
     fflush(stdout);
 }
 
+void MediaVideoToolBoxVideoHelper::extractFrameInfo() {
+    // at this moment, ffmpeg should have decoded the first frame to obtain
+    // w/h/colorspace info and number of b frame buffers
+
+    if (mObtainedAuxInfo) return;
+
+    mFfmpegVideoHelper->flush();
+    MediaSnapshotState::FrameInfo frame;
+    bool success = mFfmpegVideoHelper->receiveFrame(&frame);
+    if (success) {
+        mColorAspects = frame.color;
+        mVtbBufferSize = mFfmpegVideoHelper->frameReorderBufferSize();
+    }
+    mObtainedAuxInfo = true;
+}
+
 void MediaVideoToolBoxVideoHelper::decode(const uint8_t* frame,
                                           size_t szBytes,
                                           uint64_t inputPts) {
     VTB_DPRINT("%s(frame=%p, sz=%zu)", __func__, frame, szBytes);
+    if (!mObtainedAuxInfo) {
+        // uses ffmpeg to preprocess until vtb is ready
+        VTB_DPRINT("before ffmpeg decode");
+        mFfmpegVideoHelper->decode(frame, szBytes, inputPts);
+        VTB_DPRINT("after ffmpeg decode");
+    }
 
     parseInputFrames(frame, szBytes);
 
     // has to go in the FIFO order
     for (int i = 0; i < mInputFrames.size(); ++i) {
         InputFrame& f = mInputFrames[i];
+        bool isidr=false;
         switch (f.type) {
             case H264NaluType::SPS:
                 mSPS.assign(f.data, f.data + f.size);
@@ -114,8 +139,13 @@
                 break;
             case H264NaluType::CodedSliceIDR:
                 VTB_DPRINT("this is an IDR frame");
+                isidr=true;
             case H264NaluType::CodedSliceNonIDR:
+                if(isidr==false) {
+                    VTB_DPRINT("this is an non-IDR frame");
+                }
                 if (!mVtbReady) {
+                    extractFrameInfo();
                     createCMFormatDescription();
                     Boolean needNewSession = ( VTDecompressionSessionCanAcceptFormatDescription(mDecoderSession, mCmFmtDesc) == false);
                     if (needNewSession) {
@@ -203,11 +233,13 @@
 void MediaVideoToolBoxVideoHelper::parseInputFrames(const uint8_t* frame,
                                                     size_t sz) {
     mInputFrames.clear();
+    VTB_DPRINT("input frame %d bytes", (int)sz);
     while (1) {
         const uint8_t* remainingFrame = parseOneFrame(frame, sz);
         if (remainingFrame == nullptr)
             break;
         int consumed = (remainingFrame - frame);
+        VTB_DPRINT("consumed %d bytes", consumed);
         frame = remainingFrame;
         sz = sz - consumed;
     }
@@ -238,8 +270,11 @@
     // frame as one frame from guest, we need to separate SEI out from IDR:
     // video tool box cannot handle SEI+IDR combo-frame; for other cases, there
     // is no need to check.
-    if (H264NaluType::SEI ==
-        H264NaluParser::getFrameNaluType(frame, szBytes, nullptr)) {
+    if (H264NaluType::SEI == H264NaluParser::getFrameNaluType(frame, szBytes, nullptr)
+     || H264NaluType::SPS == H264NaluParser::getFrameNaluType(frame, szBytes, nullptr)
+     || H264NaluType::PPS == H264NaluParser::getFrameNaluType(frame, szBytes, nullptr)
+
+        ) {
         nextNalu = H264NaluParser::getNextStartCodeHeader(currentNalu + 3,
                                                           remaining - 3);
         if (nextNalu != nullptr) {
@@ -646,7 +681,7 @@
         copyFrameToCPU();
     }
 
-    if (mVtbBufferMap.size() >= mVtbBufferSize) {
+    if (mVtbBufferMap.size() > mVtbBufferSize) {
         mSavedDecodedFrames.push_back(std::move(mVtbBufferMap.begin()->second));
         mVtbBufferMap.erase(mVtbBufferMap.begin());
     }
diff --git a/android/android-emu/android/emulation/MediaVideoToolBoxVideoHelper.h b/android/android-emu/android/emulation/MediaVideoToolBoxVideoHelper.h
index f721dd9..3cfee1e 100644
--- a/android/android-emu/android/emulation/MediaVideoToolBoxVideoHelper.h
+++ b/android/android-emu/android/emulation/MediaVideoToolBoxVideoHelper.h
@@ -174,6 +174,9 @@
     // TODO: replace it with webrtc h264 parser once it is built
     // for all platforms
     std::unique_ptr<MediaFfmpegVideoHelper> mFfmpegVideoHelper;
+    // true once ffmpeg has processed first IDR
+    bool mObtainedAuxInfo {false};
+    void extractFrameInfo();
 
     // vtb decoder does not reorder output frames, that means
     // the video could see jumps all the times
diff --git a/android/android-emu/android/userspace-boot-properties.cpp b/android/android-emu/android/userspace-boot-properties.cpp
index d55b3ed..911d088 100644
--- a/android/android-emu/android/userspace-boot-properties.cpp
+++ b/android/android-emu/android/userspace-boot-properties.cpp
@@ -11,6 +11,9 @@
 
 #include "android/userspace-boot-properties.h"
 
+#include <algorithm>
+#include <string>
+
 #include "android/base/StringFormat.h"
 #include "android/featurecontrol/FeatureControl.h"
 #include "android/version.h"
@@ -23,6 +26,7 @@
         "/sys/bus/platform/devices/ANDR0001:00/properties/android/";
 static const char kSysfsAndroidDtDirDtb[] =
         "/proc/device-tree/firmware/android/";
+
 }  // namespace
 
 using android::base::StringFormat;
@@ -47,7 +51,77 @@
     const bool isX86ish = !strcmp(targetArch, "x86") || !strcmp(targetArch, "x86_64");
     const bool hasShellConsole = opts->logcat || opts->shell;
 
+    const char* checkjniProp;
+    const char* bootanimProp;
+    const char* bootanimPropValue;
+    const char* qemuGlesProp;
+    const char* qemuScreenOffTimeoutProp;
+    const char* qemuEncryptProp;
+    const char* qemuMediaProfileVideoProp;
+    const char* qemuVsyncProp;
+    const char* qemuGltransportNameProp;
+    const char* qemuDrawFlushIntervalProp;
+    const char* qemuOpenglesVersionProp;
+    const char* qemuUirendererProp;
+    const char* dalvikVmHeapsizeProp;
+    const char* qemuLegacyFakeCameraProp;
+    const char* qemuCameraProtocolVerProp;
+    const char* qemuDisplaySettingsXmlProp;
+    const char* qemuVirtioWifiProp;
+    const char* qemuWifiProp;
+    const char* androidQemudProp;
+    const char* qemuHwcodecAvcdecProp;
+    const char* qemuHwcodecVpxdecProp;
+    const char* androidbootLogcatProp;
+
     namespace fc = android::featurecontrol;
+    if (fc::isEnabled(fc::AndroidbootProps)) {
+        checkjniProp = "androidboot.dalvik.vm.checkjni";
+        bootanimProp = "androidboot.debug.sf.nobootanimation";
+        bootanimPropValue = "1";
+        qemuGlesProp = nullptr;  // deprecated
+        qemuScreenOffTimeoutProp = "androidboot.qemu.settings.system.screen_off_timeout";
+        qemuEncryptProp = nullptr;  // deprecated
+        qemuMediaProfileVideoProp = nullptr;  // deprecated
+        qemuVsyncProp = "androidboot.qemu.vsync";
+        qemuGltransportNameProp = "androidboot.qemu.gltransport.name";
+        qemuDrawFlushIntervalProp = "androidboot.qemu.gltransport.drawFlushInterval";
+        qemuOpenglesVersionProp = "androidboot.opengles.version";
+        qemuUirendererProp = "androidboot.debug.hwui.renderer";
+        dalvikVmHeapsizeProp = "androidboot.dalvik.vm.heapsize";
+        qemuLegacyFakeCameraProp = "androidboot.qemu.legacy_fake_camera";
+        qemuCameraProtocolVerProp = "androidboot.qemu.camera_protocol_ver";
+        qemuDisplaySettingsXmlProp = nullptr;  // deprecated
+        qemuVirtioWifiProp = "androidboot.qemu.virtiowifi";
+        qemuWifiProp = "androidboot.qemu.wifi";
+        androidQemudProp = nullptr;  // deprecated
+        qemuHwcodecAvcdecProp = "androidboot.qemu.hwcodec.avcdec";
+        qemuHwcodecVpxdecProp = "androidboot.qemu.hwcodec.vpxdec";
+        androidbootLogcatProp = "androidboot.logcat";
+    } else {
+        checkjniProp = "android.checkjni";
+        bootanimProp = "android.bootanim";
+        bootanimPropValue = "0";
+        qemuGlesProp = "qemu.gles";
+        qemuScreenOffTimeoutProp = "qemu.settings.system.screen_off_timeout";
+        qemuEncryptProp = "qemu.encrypt";
+        qemuMediaProfileVideoProp = "qemu.mediaprofile.video";
+        qemuVsyncProp = "qemu.vsync";
+        qemuGltransportNameProp = "qemu.gltransport";
+        qemuDrawFlushIntervalProp = "qemu.gltransport.drawFlushInterval";
+        qemuOpenglesVersionProp = "qemu.opengles.version";
+        qemuUirendererProp = "qemu.uirenderer";
+        dalvikVmHeapsizeProp = "qemu.dalvik.vm.heapsize";
+        qemuLegacyFakeCameraProp = "qemu.legacy_fake_camera";
+        qemuCameraProtocolVerProp = "qemu.camera_protocol_ver";
+        qemuDisplaySettingsXmlProp = "qemu.display.settings.xml";
+        qemuVirtioWifiProp = "qemu.virtiowifi";
+        qemuWifiProp = "qemu.wifi";
+        androidQemudProp = "android.qemud";
+        qemuHwcodecAvcdecProp = "qemu.hwcodec.avcdec";
+        qemuHwcodecVpxdecProp = "qemu.hwcodec.vpxdec";
+        androidbootLogcatProp = nullptr;
+    }
 
     std::vector<std::pair<std::string, std::string>> params;
 
@@ -65,75 +139,89 @@
     }
 
     if (!opts->no_jni) {
-        params.push_back({"android.checkjni", "1"});
+        params.push_back({checkjniProp, "1"});
     }
     if (opts->no_boot_anim) {
-        params.push_back({"android.bootanim", "0"});
+        params.push_back({bootanimProp, bootanimPropValue});
     }
 
     // qemu.gles is used to pass the GPU emulation mode to the guest
     // through kernel parameters. Note that the ro.opengles.version
     // boot property must also be defined for |gles > 0|, but this
     // is not handled here (see vl-android.c for QEMU1).
-    {
+    if (qemuGlesProp) {
         int gles;
         switch (glesMode) {
             case kAndroidGlesEmulationHost: gles = 1; break;
             case kAndroidGlesEmulationGuest: gles = 2; break;
             default: gles = 0;
         }
-        params.push_back({"qemu.gles", StringFormat("%d", gles)});
+        params.push_back({qemuGlesProp, StringFormat("%d", gles)});
     }
 
     // To save battery, set the screen off timeout to a high value.
     // Using int32_max here. The unit is milliseconds.
-    params.push_back({"qemu.settings.system.screen_off_timeout", "2147483647"}); // 596 hours
+    params.push_back({qemuScreenOffTimeoutProp, "2147483647"}); // 596 hours
 
-    if (isQemu2 && fc::isEnabled(fc::EncryptUserData)) {
-        params.push_back({"qemu.encrypt", "1"});
+    if (isQemu2 && fc::isEnabled(fc::EncryptUserData) && qemuEncryptProp) {
+        params.push_back({qemuEncryptProp, "1"});
     }
 
     // Android media profile selection
     // 1. If the SelectMediaProfileConfig is on, then select
     // <media_profile_name> if the resolution is above 1080p (1920x1080).
-    if (isQemu2 && fc::isEnabled(fc::DynamicMediaProfile)) {
+    if (isQemu2 && fc::isEnabled(fc::DynamicMediaProfile) && qemuMediaProfileVideoProp) {
         if ((lcd_width > 1920 && lcd_height > 1080) ||
             (lcd_width > 1080 && lcd_height > 1920)) {
             fprintf(stderr, "Display resolution > 1080p. Using different media profile.\n");
             params.push_back({
-                "qemu.mediaprofile.video",
+                qemuMediaProfileVideoProp,
                 "/data/vendor/etc/media_codecs_google_video_v2.xml"
             });
         }
     }
 
     // Set vsync rate
-    params.push_back({"qemu.vsync", StringFormat("%u", lcd_vsync)});
+    params.push_back({qemuVsyncProp, StringFormat("%u", lcd_vsync)});
 
     // Set gl transport props
-    params.push_back({"qemu.gltransport", gltransport});
+    params.push_back({qemuGltransportNameProp, gltransport});
     params.push_back({
-        "qemu.gltransport.drawFlushInterval",
+        qemuDrawFlushIntervalProp,
         StringFormat("%u", gltransport_drawFlushInterval)});
 
     // OpenGL ES related setup
     // 1. Set opengles.version and set Skia as UI renderer if
     // GLESDynamicVersion = on (i.e., is a reasonably good driver)
     params.push_back({
-        "qemu.opengles.version",
+        qemuOpenglesVersionProp,
         StringFormat("%d", bootPropOpenglesVersion)
     });
 
     if (fc::isEnabled(fc::GLESDynamicVersion)) {
-        params.push_back({"qemu.uirenderer", "skiagl"});
+        params.push_back({qemuUirendererProp, "skiagl"});
     }
 
-    if (opts->logcat) {
-        std::string param = opts->logcat;
-        // Replace any space with a comma.
-        std::replace(param.begin(), param.end(), ' ', ',');
-        std::replace(param.begin(), param.end(), '\t', ',');
-        params.push_back({"androidboot.logcat", param});
+    if (androidbootLogcatProp) {
+        if (opts->logcat) {
+            std::string param = opts->logcat;
+
+            // Replace any space with a comma.
+            std::replace_if(param.begin(), param.end(), [](char c){
+                switch (c) {
+                case ' ':
+                case '\t':
+                    return true;
+
+                default:
+                    return false;
+                }
+            }, ',');
+
+            params.push_back({androidbootLogcatProp, param});
+        } else {
+            params.push_back({androidbootLogcatProp, "*:V"});
+        }
     }
 
     if (opts->bootchart) {
@@ -146,17 +234,17 @@
 
     if (vm_heapSize > 0) {
         params.push_back({
-            "qemu.dalvik.vm.heapsize",
+            dalvikVmHeapsizeProp,
             StringFormat("%dm", vm_heapSize)
         });
     }
 
     if (opts->legacy_fake_camera) {
-        params.push_back({"qemu.legacy_fake_camera", "1"});
+        params.push_back({qemuLegacyFakeCameraProp, "1"});
     }
 
     if (apiLevel > 29) {
-        params.push_back({"qemu.camera_protocol_ver", "1"});
+        params.push_back({qemuCameraProtocolVerProp, "1"});
     }
 
     const bool isDynamicPartition = fc::isEnabled(fc::DynamicPartition);
@@ -182,21 +270,21 @@
     }
 
     // display settings file name
-    if (displaySettingsXml && displaySettingsXml[0]) {
-        params.push_back({"qemu.display.settings.xml", displaySettingsXml});
+    if (displaySettingsXml && displaySettingsXml[0] && qemuDisplaySettingsXmlProp) {
+        params.push_back({qemuDisplaySettingsXmlProp, displaySettingsXml});
     }
 
     if (isQemu2) {
         if (fc::isEnabled(fc::VirtioWifi)) {
-            params.push_back({"qemu.virtiowifi", "1"});
+            params.push_back({qemuVirtioWifiProp, "1"});
         } else if (fc::isEnabled(fc::Wifi)) {
-            params.push_back({"qemu.wifi", "1"});
+            params.push_back({qemuWifiProp, "1"});
         }
     }
 
     if (fc::isEnabled(fc::HardwareDecoder)) {
-        params.push_back({"qemu.hwcodec.avcdec", "2"});
-        params.push_back({"qemu.hwcodec.vpxdec", "2"});
+        params.push_back({qemuHwcodecAvcdecProp, "2"});
+        params.push_back({qemuHwcodecVpxdecProp, "2"});
     }
 
     if (isQemu2) {
@@ -207,7 +295,9 @@
             });
         }
 
-        params.push_back({"android.qemud", "1"});
+        if (androidQemudProp) {
+            params.push_back({androidQemudProp, "1"});
+        }
     } else {  // !isQemu2
         // Technical note: There are several important constraints when
         // setting up QEMU1 virtual ttys:
@@ -247,10 +337,9 @@
 
         int logcatSerial = 1;
         if (apiLevel < 14) {
-            params.push_back({
-                "android.qemud",
-                StringFormat("%s1", kernelSerialPrefix)
-            });
+            if (androidQemudProp) {
+                params.push_back({androidQemudProp, StringFormat("%s1", kernelSerialPrefix)});
+            }
 
             if (isX86ish) {
                 logcatSerial = 0;
@@ -260,7 +349,9 @@
         } else {
             // The rild daemon, used for GSM emulation, checks for qemud,
             // just set it to a dummy value instead of a serial port.
-            params.push_back({"android.qemud", "1"});
+            if (androidQemudProp) {
+                params.push_back({androidQemudProp, "1"});
+            }
         }
 
         if (hasShellConsole) {
diff --git a/android/android-emu/android/userspace-boot-properties_unittest.cpp b/android/android-emu/android/userspace-boot-properties_unittest.cpp
index ec288da..e982dfd 100644
--- a/android/android-emu/android/userspace-boot-properties_unittest.cpp
+++ b/android/android-emu/android/userspace-boot-properties_unittest.cpp
@@ -11,6 +11,7 @@
 
 #include "android/userspace-boot-properties.h"
 #include "android/base/misc/StringUtils.h"
+#include "android/featurecontrol/FeatureControl.h"
 
 #include <map>
 #include <gtest/gtest.h>
@@ -27,7 +28,9 @@
 
 using android::base::join;
 
-TEST(getUserspaceBootProperties, Simple) {
+namespace fc = android::featurecontrol;
+
+TEST(getUserspaceBootProperties, BootconfigOff) {
     const std::vector<std::string> verifiedBootParameters = {
         "verifiedBootParameters.foo=value"
     };
@@ -38,6 +41,8 @@
     opts.bootchart = (char*)"bootchart";
     opts.selinux = (char*)"selinux";
 
+    fc::setEnabledOverride(fc::AndroidbootProps, false);
+
     const auto params = getUserspaceBootProperties(
         &opts,
         "x86_64",                   // targetArch
@@ -71,7 +76,7 @@
     EXPECT_STREQ(paramsMap["qemu.gltransport"].c_str(), "gltransport");
     EXPECT_STREQ(paramsMap["qemu.gltransport.drawFlushInterval"].c_str(), "77");
     EXPECT_STREQ(paramsMap["qemu.opengles.version"].c_str(), "123");
-    EXPECT_STREQ(paramsMap["androidboot.logcat"].c_str(), "logcat");
+    EXPECT_STREQ(paramsMap["androidboot.logcat"].c_str(), "");
     EXPECT_STREQ(paramsMap["androidboot.bootchart"].c_str(), "bootchart");
     EXPECT_STREQ(paramsMap["androidboot.selinux"].c_str(), "selinux");
     EXPECT_STREQ(paramsMap["qemu.dalvik.vm.heapsize"].c_str(), "64m");
@@ -79,4 +84,54 @@
     EXPECT_STREQ(paramsMap["qemu.display.settings.xml"].c_str(), "displaySettingsXml");
 }
 
+TEST(getUserspaceBootProperties, BootconfigOn) {
+    const std::vector<std::string> verifiedBootParameters = {
+        "verifiedBootParameters.foo=value"
+    };
+
+    AndroidOptions opts = makeAndroidOptions();
+
+    opts.logcat = (char*)"logcat";
+    opts.bootchart = (char*)"bootchart";
+    opts.selinux = (char*)"selinux";
+
+    fc::setEnabledOverride(fc::AndroidbootProps, true);
+
+    const auto params = getUserspaceBootProperties(
+        &opts,
+        "x86_64",                   // targetArch
+        "serialno",                 // serialno
+        true,                       // isQemu2
+        600,                        // lcd_width
+        800,                        // lcd_height
+        60,                         // lcd_vsync
+        kAndroidGlesEmulationHost,  // glesMode
+        123,                        // bootPropOpenglesVersion
+        64,                         // vm_heapSize
+        29,                         // apiLevel
+        "kernelSerialPrefix",       // kernelSerialPrefix
+        &verifiedBootParameters,    // verifiedBootParameters
+        "gltransport",              // gltransport
+        77,                         // gltransport_drawFlushInterval
+        "displaySettingsXml");      // displaySettingsXml
+
+    std::map<std::string, std::string> paramsMap;
+    for (const auto& kv : params) {
+        EXPECT_TRUE(paramsMap.insert(kv).second);
+    }
+
+    EXPECT_STREQ(paramsMap["qemu"].c_str(), "1");
+    EXPECT_STREQ(paramsMap["androidboot.hardware"].c_str(), "ranchu");
+    EXPECT_STREQ(paramsMap["androidboot.serialno"].c_str(), "serialno");
+    EXPECT_STREQ(paramsMap["androidboot.dalvik.vm.checkjni"].c_str(), "1");
+    EXPECT_STREQ(paramsMap["androidboot.qemu.gltransport.name"].c_str(), "gltransport");
+    EXPECT_STREQ(paramsMap["androidboot.qemu.gltransport.drawFlushInterval"].c_str(), "77");
+    EXPECT_STREQ(paramsMap["androidboot.opengles.version"].c_str(), "123");
+    EXPECT_STREQ(paramsMap["androidboot.logcat"].c_str(), "logcat");
+    EXPECT_STREQ(paramsMap["androidboot.bootchart"].c_str(), "bootchart");
+    EXPECT_STREQ(paramsMap["androidboot.selinux"].c_str(), "selinux");
+    EXPECT_STREQ(paramsMap["androidboot.dalvik.vm.heapsize"].c_str(), "64m");
+    EXPECT_STREQ(paramsMap["verifiedBootParameters.foo"].c_str(), "value");
+}
+
 }  // namespace android
diff --git a/android/data/advancedFeatures.ini b/android/data/advancedFeatures.ini
index 75eef6e..2ae5a3b 100644
--- a/android/data/advancedFeatures.ini
+++ b/android/data/advancedFeatures.ini
@@ -388,4 +388,4 @@
 # If enabled, the boot userspace properties (e.g. `qemu=1` or
 # `qemu.opengles.version=123456`) are passed in the ramdisk instead of
 # the kernel command line (which is deprecated for these purposes).
-AndroidbootProps = off
+AndroidbootProps = on
diff --git a/android/data/advancedFeaturesCanary.ini b/android/data/advancedFeaturesCanary.ini
index 6398521..f137894 100644
--- a/android/data/advancedFeaturesCanary.ini
+++ b/android/data/advancedFeaturesCanary.ini
@@ -393,4 +393,4 @@
 # If enabled, the boot userspace properties (e.g. `qemu=1` or
 # `qemu.opengles.version=123456`) are passed in the ramdisk instead of
 # the kernel command line (which is deprecated for these purposes).
-AndroidbootProps = off
+AndroidbootProps = on