Snap for 8512216 from b371a0e5b9e88c319522a6fa74e036e8fb983a0c to tm-frc-art-release

Change-Id: Id0f6c7e6df6a673e38ccca0efdf5742809edc3e4
diff --git a/Android.bp b/Android.bp
index 25b1185..08df261 100644
--- a/Android.bp
+++ b/Android.bp
@@ -152,6 +152,11 @@
         "-g",
     ],
 
+    tidy_timeout_srcs: [
+        "aidl_unittest.cpp",
+        "options_unittest.cpp",
+    ],
+
     srcs: [
         "aidl_unittest.cpp",
         "ast_java_unittest.cpp",
@@ -173,8 +178,12 @@
         "liblog",
     ],
 
-    sanitize: {
-        address: true,
+    target: {
+        host: {
+            sanitize: {
+                address: true,
+            },
+        },
     },
 
     required: ["aidl-golden-test-build-hook"],
@@ -303,8 +312,6 @@
         "tests/android/aidl/tests/UnionWithFd.aidl",
         "tests/android/aidl/tests/extension/*.aidl",
         "tests/android/aidl/tests/nested/*.aidl",
-        "tests/android/aidl/tests/permission/IProtected.aidl",
-        "tests/android/aidl/tests/permission/IProtectedInterface.aidl",
         "tests/android/aidl/tests/unions/*.aidl",
     ],
     path: "tests",
@@ -321,8 +328,10 @@
 }
 
 filegroup {
-    name: "libaidl-integration-platform-test-files",
+    name: "libaidl-integration-permission-test-files",
     srcs: [
+        "tests/android/aidl/tests/permission/IProtected.aidl",
+        "tests/android/aidl/tests/permission/IProtectedInterface.aidl",
         "tests/android/aidl/tests/permission/platform/*.aidl",
     ],
     path: "tests",
@@ -395,6 +404,10 @@
         "aidl_test_loggable_interface-cpp",
         "aidl-test-fixedsizearray-cpp",
     ],
+    tidy_timeout_srcs: [
+        "tests/aidl_test_client_parcelables.cpp",
+        "tests/aidl_test_client_primitives.cpp",
+    ],
     srcs: [
         "tests/aidl_test_client.cpp",
         "tests/aidl_test_client_builtin_transactions.cpp",
@@ -429,6 +442,7 @@
         "libbinder_ndk",
     ],
     srcs: [
+        "tests/aidl_test_client_ndk_delegate.cpp",
         "tests/aidl_test_client_ndk_loggable_interface.cpp",
         "tests/aidl_test_client_ndk_nested.cpp",
         "tests/aidl_test_client_ndk_nullables.cpp",
@@ -636,7 +650,7 @@
         ":aidl_test_loggable_interface-cpp-source",
         ":aidl_test_loggable_interface-java-source",
         ":aidl_test_loggable_interface-ndk-source",
-        ":aidl-test-interface-platform-java-source",
+        ":aidl-test-interface-permission-java-source",
         ":aidl-test-fixedsizearray-cpp-source",
         ":aidl-test-fixedsizearray-java-source",
         ":aidl-test-fixedsizearray-ndk-source",
@@ -704,10 +718,10 @@
 }
 
 aidl_interface {
-    name: "aidl-test-interface-platform",
+    name: "aidl-test-interface-permission",
     unstable: true,
     flags: ["-Werror"],
-    srcs: [":libaidl-integration-platform-test-files"],
+    srcs: [":libaidl-integration-permission-test-files"],
     include_dirs: [
         "frameworks/base/core/java",
     ],
diff --git a/OWNERS b/OWNERS
index cc35b11..a1fcf5f 100644
--- a/OWNERS
+++ b/OWNERS
@@ -2,6 +2,5 @@
 jiyong@google.com
 jooyung@google.com
 malchev@google.com
-sadmac@google.com
 smoreland@google.com
 vmarko@google.com
diff --git a/aidl.cpp b/aidl.cpp
index fd63c6a..6938b53 100644
--- a/aidl.cpp
+++ b/aidl.cpp
@@ -578,18 +578,18 @@
       if (options.Version() > 0) {
         auto ret = typenames->MakeResolvedType(AIDL_LOCATION_HERE, "int", false);
         vector<unique_ptr<AidlArgument>>* args = new vector<unique_ptr<AidlArgument>>();
-        auto method = std::make_unique<AidlMethod>(
-            AIDL_LOCATION_HERE, false, ret.release(), "getInterfaceVersion", args, Comments{},
-            kGetInterfaceVersionId, false /* is_user_defined */);
+        auto method = std::make_unique<AidlMethod>(AIDL_LOCATION_HERE, false, ret.release(),
+                                                   "getInterfaceVersion", args, Comments{},
+                                                   kGetInterfaceVersionId);
         interface->AddMethod(std::move(method));
       }
       // add the meta-method 'string getInterfaceHash()' if hash is specified.
       if (!options.Hash().empty()) {
         auto ret = typenames->MakeResolvedType(AIDL_LOCATION_HERE, "String", false);
         vector<unique_ptr<AidlArgument>>* args = new vector<unique_ptr<AidlArgument>>();
-        auto method = std::make_unique<AidlMethod>(
-            AIDL_LOCATION_HERE, false, ret.release(), kGetInterfaceHash, args, Comments{},
-            kGetInterfaceHashId, false /* is_user_defined */);
+        auto method =
+            std::make_unique<AidlMethod>(AIDL_LOCATION_HERE, false, ret.release(),
+                                         kGetInterfaceHash, args, Comments{}, kGetInterfaceHashId);
         interface->AddMethod(std::move(method));
       }
       if (!CheckAndAssignMethodIDs(interface->GetMethods())) {
diff --git a/aidl_const_expressions.cpp b/aidl_const_expressions.cpp
index 0288a60..afab3a2 100644
--- a/aidl_const_expressions.cpp
+++ b/aidl_const_expressions.cpp
@@ -89,14 +89,14 @@
       mOverflowed = true;
       return 0;
     }
-    return mValue / o;
+    return static_cast<T>(mValue / o);
   }
   T operator%(T o) {
     if (o == 0 || (isMin() && o == -1)) {
       mOverflowed = true;
       return 0;
     }
-    return mValue % o;
+    return static_cast<T>(mValue % o);
   }
   T operator|(T o) { return mValue | o; }
   T operator^(T o) { return mValue ^ o; }
@@ -112,14 +112,14 @@
       mOverflowed = true;
       return 0;
     }
-    return mValue >> o;
+    return static_cast<T>(mValue >> o);
   }
   T operator<<(T o) {
     if (o < 0 || mValue < 0 || o > CLZ(mValue) || o >= static_cast<T>(sizeof(T) * 8)) {
       mOverflowed = true;
       return 0;
     }
-    return mValue << o;
+    return static_cast<T>(mValue << o);
   }
   T operator||(T o) { return mValue || o; }
   T operator&&(T o) { return mValue && o; }
diff --git a/aidl_dumpapi.cpp b/aidl_dumpapi.cpp
index 7d3f19d..1451d41 100644
--- a/aidl_dumpapi.cpp
+++ b/aidl_dumpapi.cpp
@@ -32,6 +32,9 @@
 namespace aidl {
 
 void DumpVisitor::DumpType(const AidlDefinedType& dt, const string& type) {
+  if (!dt.IsUserDefined()) {
+    return;
+  }
   DumpComments(dt);
   DumpAnnotations(dt);
   out << type << " " << dt.GetName();
@@ -47,7 +50,6 @@
 
 void DumpVisitor::DumpMembers(const AidlDefinedType& dt) {
   for (const auto& method : dt.GetMethods()) {
-    if (!method->IsUserDefined()) continue;
     method->DispatchVisit(*this);
   }
   for (const auto& field : dt.GetFields()) {
@@ -95,21 +97,29 @@
 void DumpVisitor::Visit(const AidlInterface& t) {
   DumpType(t, "interface");
 }
+
 void DumpVisitor::Visit(const AidlParcelable& t) {
   DumpType(t, "parcelable");
 }
+
 void DumpVisitor::Visit(const AidlStructuredParcelable& t) {
   DumpType(t, "parcelable");
 }
+
 void DumpVisitor::Visit(const AidlUnionDecl& t) {
   DumpType(t, "union");
 }
+
 void DumpVisitor::Visit(const AidlEnumDeclaration& t) {
+  if (!t.IsUserDefined()) {
+    return;
+  }
   DumpComments(t);
   DumpAnnotations(t);
   out << "enum " << t.GetName() << " {\n";
   out.Indent();
   for (const auto& e : t.GetEnumerators()) {
+    DumpComments(*e);
     out << e->GetName() << " = ";
     DumpConstantValue(t.GetBackingType(), *e->GetValue());
     out << ",\n";
@@ -119,10 +129,17 @@
 }
 
 void DumpVisitor::Visit(const AidlMethod& m) {
+  if (!m.IsUserDefined()) {
+    return;
+  }
   DumpComments(m);
   out << m.ToString() << ";\n";
 }
+
 void DumpVisitor::Visit(const AidlVariableDeclaration& v) {
+  if (!v.IsUserDefined()) {
+    return;
+  }
   DumpComments(v);
   Visit(v.GetType());
   if (v.IsDefaultUserSpecified()) {
@@ -133,7 +150,11 @@
     out << " " << v.GetName() << ";\n";
   }
 }
+
 void DumpVisitor::Visit(const AidlConstantDeclaration& c) {
+  if (!c.IsUserDefined()) {
+    return;
+  }
   DumpComments(c);
   out << "const ";
   Visit(c.GetType());
diff --git a/aidl_language.cpp b/aidl_language.cpp
index e6b4b32..6e19d6e 100644
--- a/aidl_language.cpp
+++ b/aidl_language.cpp
@@ -143,7 +143,6 @@
        "JavaOnlyStableParcelable",
        CONTEXT_TYPE_UNSTRUCTURED_PARCELABLE,
        {}},
-      {AidlAnnotation::Type::HIDE, "Hide", CONTEXT_TYPE | CONTEXT_MEMBER, {}},
       {AidlAnnotation::Type::BACKING,
        "Backing",
        CONTEXT_TYPE_ENUM,
@@ -155,7 +154,7 @@
        /* repeatable= */ true},
       {AidlAnnotation::Type::JAVA_DERIVE,
        "JavaDerive",
-       CONTEXT_TYPE_STRUCTURED_PARCELABLE | CONTEXT_TYPE_UNION,
+       CONTEXT_TYPE_STRUCTURED_PARCELABLE | CONTEXT_TYPE_UNION | CONTEXT_TYPE_ENUM,
        {{"toString", kBooleanType}, {"equals", kBooleanType}}},
       {AidlAnnotation::Type::JAVA_DEFAULT, "JavaDefault", CONTEXT_TYPE_INTERFACE, {}},
       {AidlAnnotation::Type::JAVA_DELEGATOR, "JavaDelegator", CONTEXT_TYPE_INTERFACE, {}},
@@ -518,10 +517,6 @@
          GetAnnotation(annotations_, AidlAnnotation::Type::JAVA_STABLE_PARCELABLE);
 }
 
-bool AidlAnnotatable::IsHide() const {
-  return GetAnnotation(annotations_, AidlAnnotation::Type::HIDE);
-}
-
 bool AidlAnnotatable::JavaDerive(const std::string& method) const {
   auto annotation = GetAnnotation(annotations_, AidlAnnotation::Type::JAVA_DERIVE);
   if (annotation != nullptr) {
@@ -1068,20 +1063,19 @@
 AidlMethod::AidlMethod(const AidlLocation& location, bool oneway, AidlTypeSpecifier* type,
                        const std::string& name, std::vector<std::unique_ptr<AidlArgument>>* args,
                        const Comments& comments)
-    : AidlMethod(location, oneway, type, name, args, comments, 0, true) {
+    : AidlMethod(location, oneway, type, name, args, comments, 0) {
   has_id_ = false;
 }
 
 AidlMethod::AidlMethod(const AidlLocation& location, bool oneway, AidlTypeSpecifier* type,
                        const std::string& name, std::vector<std::unique_ptr<AidlArgument>>* args,
-                       const Comments& comments, int id, bool is_user_defined)
+                       const Comments& comments, int id)
     : AidlMember(location, comments),
       oneway_(oneway),
       type_(type),
       name_(name),
       arguments_(std::move(*args)),
-      id_(id),
-      is_user_defined_(is_user_defined) {
+      id_(id) {
   has_id_ = true;
   delete args;
   for (const unique_ptr<AidlArgument>& a : arguments_) {
diff --git a/aidl_language.h b/aidl_language.h
index c73618f..15ce4bb 100644
--- a/aidl_language.h
+++ b/aidl_language.h
@@ -165,6 +165,7 @@
   static void ClearUnvisitedNodes();
   static const std::vector<AidlLocation>& GetLocationsOfUnvisitedNodes();
   void MarkVisited() const;
+  bool IsUserDefined() const { return !GetLocation().IsInternal(); }
 
  private:
   std::string PrintLine() const;
@@ -224,7 +225,6 @@
  public:
   enum class Type {
     BACKING = 1,
-    HIDE,
     JAVA_STABLE_PARCELABLE,
     UNSUPPORTED_APP_USAGE,
     VINTF_STABILITY,
@@ -354,7 +354,6 @@
   bool IsJavaOnlyImmutable() const;
   bool IsFixedSize() const;
   bool IsStableApiParcelable(Options::Language lang) const;
-  bool IsHide() const;
   bool JavaDerive(const std::string& method) const;
   bool IsJavaDefault() const;
   bool IsJavaDelegator() const;
@@ -854,8 +853,7 @@
   AidlMethod(const AidlLocation& location, bool oneway, AidlTypeSpecifier* type, const string& name,
              vector<unique_ptr<AidlArgument>>* args, const Comments& comments);
   AidlMethod(const AidlLocation& location, bool oneway, AidlTypeSpecifier* type, const string& name,
-             vector<unique_ptr<AidlArgument>>* args, const Comments& comments, int id,
-             bool is_user_defined = true);
+             vector<unique_ptr<AidlArgument>>* args, const Comments& comments, int id);
   virtual ~AidlMethod() = default;
 
   // non-copyable, non-movable
@@ -877,8 +875,6 @@
   int GetId() const { return id_; }
   void SetId(unsigned id) { id_ = id; }
 
-  bool IsUserDefined() const { return is_user_defined_; }
-
   const std::vector<std::unique_ptr<AidlArgument>>& GetArguments() const {
     return arguments_;
   }
@@ -920,7 +916,6 @@
   std::vector<const AidlArgument*> out_arguments_;
   bool has_id_;
   int id_;
-  bool is_user_defined_ = true;
 };
 
 // AidlDefinedType represents either an interface, parcelable, or enum that is
@@ -1004,10 +999,6 @@
     return constants_;
   }
   const std::vector<std::unique_ptr<AidlMethod>>& GetMethods() const { return methods_; }
-  void AddMethod(std::unique_ptr<AidlMethod> method) {
-    members_.push_back(method.get());
-    methods_.push_back(std::move(method));
-  }
   const std::vector<const AidlMember*>& GetMembers() const { return members_; }
   void TraverseChildren(std::function<void(const AidlNode&)> traverse) const override {
     AidlAnnotatable::TraverseChildren(traverse);
@@ -1016,6 +1007,17 @@
     }
   }
 
+  // Modifiers
+  void AddMethod(std::unique_ptr<AidlMethod> method) {
+    members_.push_back(method.get());
+    methods_.push_back(std::move(method));
+  }
+  void AddType(std::unique_ptr<AidlDefinedType> type) {
+    type->SetEnclosingScope(this);
+    members_.push_back(type.get());
+    types_.push_back(std::move(type));
+  }
+
  protected:
   // utility for subclasses with getter names
   bool CheckValidForGetterNames() const;
diff --git a/aidl_language_l.ll b/aidl_language_l.ll
index 34e84f1..5b11008 100644
--- a/aidl_language_l.ll
+++ b/aidl_language_l.ll
@@ -22,6 +22,14 @@
 #include "parser.h"
 #include "aidl_language_y.h"
 
+#ifndef YYSTYPE
+#define YYSTYPE yy::parser::semantic_type
+#endif
+
+#ifndef YYLTYPE
+#define YYLTYPE yy::parser::location_type
+#endif
+
 #define YY_USER_ACTION yylloc->columns(yyleng);
 %}
 
diff --git a/aidl_to_cpp_common.cpp b/aidl_to_cpp_common.cpp
index f0df305..682b6c8 100644
--- a/aidl_to_cpp_common.cpp
+++ b/aidl_to_cpp_common.cpp
@@ -219,13 +219,26 @@
   out << " " << enum_decl.GetName() << " : " << backing_type << " {\n";
   out.Indent();
   for (const auto& enumerator : enum_decl.GetEnumerators()) {
-    out << enumerator->GetName() << " = "
-        << enumerator->ValueString(enum_decl.GetBackingType(), decorator) << ",\n";
+    out << enumerator->GetName();
+    GenerateDeprecated(out, *enumerator);
+    out << " = " << enumerator->ValueString(enum_decl.GetBackingType(), decorator) << ",\n";
   }
   out.Dedent();
   out << "};\n";
 }
 
+static bool IsEnumDeprecated(const AidlEnumDeclaration& enum_decl) {
+  if (enum_decl.IsDeprecated()) {
+    return true;
+  }
+  for (const auto& e : enum_decl.GetEnumerators()) {
+    if (e->IsDeprecated()) {
+      return true;
+    }
+  }
+  return false;
+}
+
 // enum_values template value is defined in its own namespace (android::internal or ndk::internal),
 // so the enum_decl type should be fully qualified.
 std::string GenerateEnumValues(const AidlEnumDeclaration& enum_decl,
@@ -237,9 +250,11 @@
   std::ostringstream code;
   code << "#pragma clang diagnostic push\n";
   code << "#pragma clang diagnostic ignored \"-Wc++17-extensions\"\n";
+  if (IsEnumDeprecated(enum_decl)) {
+    code << "#pragma clang diagnostic ignored \"-Wdeprecated-declarations\"\n";
+  }
   code << "template <>\n";
   code << "constexpr inline std::array<" << fq_name << ", " << size << ">";
-  GenerateDeprecated(code, enum_decl);
   code << " enum_values<" << fq_name << "> = {\n";
   for (const auto& enumerator : enum_decl.GetEnumerators()) {
     code << "  " << fq_name << "::" << enumerator->GetName() << ",\n";
@@ -255,14 +270,12 @@
                                  const std::string& backing_type) {
   const auto q_name = GetQualifiedName(enum_decl);
   std::ostringstream code;
-  const std::string signature =
-      "[[nodiscard]] static inline std::string toString(" + q_name + " val)";
-  if (enum_decl.IsDeprecated()) {
-    code << signature;
-    GenerateDeprecated(code, enum_decl);
-    code << ";\n";
+  bool is_enum_deprecated = IsEnumDeprecated(enum_decl);
+  if (is_enum_deprecated) {
+    code << "#pragma clang diagnostic push\n";
+    code << "#pragma clang diagnostic ignored \"-Wdeprecated-declarations\"\n";
   }
-  code << signature << " {\n";
+  code << "[[nodiscard]] static inline std::string toString(" + q_name + " val) {\n";
   code << "  switch(val) {\n";
   std::set<std::string> unique_cases;
   for (const auto& enumerator : enum_decl.GetEnumerators()) {
@@ -281,6 +294,9 @@
   code << "    return std::to_string(static_cast<" << backing_type << ">(val));\n";
   code << "  }\n";
   code << "}\n";
+  if (is_enum_deprecated) {
+    code << "#pragma clang diagnostic pop\n";
+  }
   return code.str();
 }
 
@@ -314,7 +330,7 @@
   }} else {{
     return (_lhs.getTag() == _Tag)
       ? _cmp_value(_lhs.get<_Tag>(), _rhs.get<_Tag>())
-      : _cmp_value_at<(Tag)(_Tag-1)>(_lhs, _rhs);
+      : _cmp_value_at<static_cast<Tag>(static_cast<size_t>(_Tag)-1)>(_lhs, _rhs);
   }}
 }}
 template <typename _Type>
@@ -409,9 +425,16 @@
   out << "os << \"" + parcelable.GetName() + "{\";\n";
   out << "switch (getTag()) {\n";
   for (const auto& f : parcelable.GetFields()) {
+    if (f->IsDeprecated()) {
+      out << "#pragma clang diagnostic push\n";
+      out << "#pragma clang diagnostic ignored \"-Wdeprecated-declarations\"\n";
+    }
     const string tag = f->GetName();
     out << "case " << tag << ": os << \"" << tag << ": \" << "
         << "::android::internal::ToString(get<" + tag + ">()); break;\n";
+    if (f->IsDeprecated()) {
+      out << "#pragma clang diagnostic pop\n";
+    }
   }
   out << "}\n";
   out << "os << \"}\";\n";
@@ -481,7 +504,7 @@
     const auto& default_value = name_of(first_field->GetType(), typenames) + "(" +
                                 first_field->ValueString(decorator) + ")";
 
-    out << "Tag _tag __attribute__((aligned (1))) = " << default_name << ";\n";
+    out << "Tag _tag = " << default_name << ";\n";
     out << "union _value_t {\n";
     out.Indent();
     out << "_value_t() {}\n";
@@ -512,23 +535,12 @@
 }
 
 void UnionWriter::PublicFields(CodeWriter& out) const {
-  std::string tag_type = "int32_t";
-  if (decl.IsFixedSize()) {
-    // For @FixedSize union, we use a smaller type for a tag to minimize the size overhead.
-    AIDL_FATAL_IF(decl.GetFields().size() > std::numeric_limits<uint8_t>::max(), decl)
-        << "Too many fields for @FixedSize";
-    tag_type = "uint8_t";
-  }
-  out << "enum Tag : " << tag_type << " {\n";
-  bool is_first = true;
+  out << "// Expose tag symbols for legacy code\n";
   for (const auto& f : decl.GetFields()) {
-    out << "  " << f->GetName();
+    out << "static const inline Tag";
     GenerateDeprecated(out, *f);
-    if (is_first) out << " = 0";
-    out << ",  // " << f->Signature() << ";\n";
-    is_first = false;
+    out << " " << f->GetName() << " = Tag::" << f->GetName() << ";\n";
   }
-  out << "};\n";
 
   const auto& name = decl.GetName();
 
@@ -540,7 +552,7 @@
     auto typelist = Join(field_types, ", ");
     auto tmpl = R"--(
 template <Tag _Tag>
-using _at = typename std::tuple_element<_Tag, std::tuple<{typelist}>>::type;
+using _at = typename std::tuple_element<static_cast<size_t>(_Tag), std::tuple<{typelist}>>::type;
 template <Tag _Tag, typename _Type>
 static {name} make(_Type&& _arg) {{
   {name} _inst;
@@ -578,7 +590,7 @@
 template<typename _Tp>
 static constexpr bool _not_self = !std::is_same_v<std::remove_cv_t<std::remove_reference_t<_Tp>>, {name}>;
 
-{name}() : _value(std::in_place_index<{default_name}>, {default_value}) {{ }}
+{name}() : _value(std::in_place_index<static_cast<size_t>({default_name})>, {default_value}) {{ }}
 
 template <typename _Tp, typename = std::enable_if_t<_not_self<_Tp>>>
 // NOLINTNEXTLINE(google-explicit-constructor)
@@ -591,12 +603,12 @@
 
 template <Tag _tag, typename... _Tp>
 static {name} make(_Tp&&... _args) {{
-  return {name}(std::in_place_index<_tag>, std::forward<_Tp>(_args)...);
+  return {name}(std::in_place_index<static_cast<size_t>(_tag)>, std::forward<_Tp>(_args)...);
 }}
 
 template <Tag _tag, typename _Tp, typename... _Up>
 static {name} make(std::initializer_list<_Tp> _il, _Up&&... _args) {{
-  return {name}(std::in_place_index<_tag>, std::move(_il), std::forward<_Up>(_args)...);
+  return {name}(std::in_place_index<static_cast<size_t>(_tag)>, std::move(_il), std::forward<_Up>(_args)...);
 }}
 
 Tag getTag() const {{
@@ -606,18 +618,18 @@
 template <Tag _tag>
 const auto& get() const {{
   if (getTag() != _tag) {{ __assert2(__FILE__, __LINE__, __PRETTY_FUNCTION__, "bad access: a wrong tag"); }}
-  return std::get<_tag>(_value);
+  return std::get<static_cast<size_t>(_tag)>(_value);
 }}
 
 template <Tag _tag>
 auto& get() {{
   if (getTag() != _tag) {{ __assert2(__FILE__, __LINE__, __PRETTY_FUNCTION__, "bad access: a wrong tag"); }}
-  return std::get<_tag>(_value);
+  return std::get<static_cast<size_t>(_tag)>(_value);
 }}
 
 template <Tag _tag, typename... _Tp>
 void set(_Tp&&... _args) {{
-  _value.emplace<_tag>(std::forward<_Tp>(_args)...);
+  _value.emplace<static_cast<size_t>(_tag)>(std::forward<_Tp>(_args)...);
 }}
 
 )--";
@@ -644,8 +656,12 @@
 
   out << fmt::format("{} {};\n", ctx.status_type, status);
   read_var(tag, *tag_type);
-  out << fmt::format("switch ({}) {{\n", tag);
+  out << fmt::format("switch (static_cast<Tag>({})) {{\n", tag);
   for (const auto& variable : decl.GetFields()) {
+    if (variable->IsDeprecated()) {
+      out << "#pragma clang diagnostic push\n";
+      out << "#pragma clang diagnostic ignored \"-Wdeprecated-declarations\"\n";
+    }
     out << fmt::format("case {}: {{\n", variable->GetName());
     out.Indent();
     const auto& type = variable->GetType();
@@ -665,6 +681,9 @@
     out << "}\n";
     out << fmt::format("return {}; }}\n", ctx.status_ok);
     out.Dedent();
+    if (variable->IsDeprecated()) {
+      out << "#pragma clang diagnostic pop\n";
+    }
   }
   out << "}\n";
   out << fmt::format("return {};\n", ctx.status_bad);
@@ -680,14 +699,21 @@
   const string status = "_aidl_ret_status";
 
   out << fmt::format("{} {} = ", ctx.status_type, status);
-  ctx.write_func(out, "getTag()", *tag_type);
+  ctx.write_func(out, "static_cast<int32_t>(getTag())", *tag_type);
   out << ";\n";
   out << fmt::format("if ({} != {}) return {};\n", status, ctx.status_ok, status);
   out << "switch (getTag()) {\n";
   for (const auto& variable : decl.GetFields()) {
+    if (variable->IsDeprecated()) {
+      out << "#pragma clang diagnostic push\n";
+      out << "#pragma clang diagnostic ignored \"-Wdeprecated-declarations\"\n";
+    }
     out << fmt::format("case {}: return ", variable->GetName());
     ctx.write_func(out, "get<" + variable->GetName() + ">()", variable->GetType());
     out << ";\n";
+    if (variable->IsDeprecated()) {
+      out << "#pragma clang diagnostic pop\n";
+    }
   }
   out << "}\n";
   out << "__assert2(__FILE__, __LINE__, __PRETTY_FUNCTION__, \"can't reach here\");\n";
diff --git a/aidl_to_java.cpp b/aidl_to_java.cpp
index 3cc2472..bc705df 100644
--- a/aidl_to_java.cpp
+++ b/aidl_to_java.cpp
@@ -135,14 +135,14 @@
 }
 
 namespace {
-string JavaSignatureOfInternal(
-    const AidlTypeSpecifier& aidl, bool instantiable, bool omit_array,
-    bool boxing = false /* boxing can be true only if it is a type parameter */) {
+string JavaSignatureOfInternal(const AidlTypeSpecifier& aidl, bool instantiable, bool omit_array,
+                               bool boxing) {
   string ret = JavaNameOf(aidl, instantiable, boxing && !aidl.IsArray());
   if (aidl.IsGeneric()) {
     vector<string> arg_names;
     for (const auto& ta : aidl.GetTypeParameters()) {
-      arg_names.emplace_back(JavaSignatureOfInternal(*ta, false, false, true /* boxing */));
+      arg_names.emplace_back(JavaSignatureOfInternal(*ta, /*instantiable=*/false,
+                                                     /*omit_array=*/false, /*boxing=*/true));
     }
     ret += "<" + Join(arg_names, ",") + ">";
   }
@@ -185,11 +185,20 @@
 }  // namespace
 
 string JavaSignatureOf(const AidlTypeSpecifier& aidl) {
-  return JavaSignatureOfInternal(aidl, false, false);
+  return JavaSignatureOfInternal(aidl, /*instantiable=*/false, /*omit_array=*/false,
+                                 /*boxing=*/false);
 }
 
+// Used for "new" expression. Ignore arrays because "new" expression handles it.
 string InstantiableJavaSignatureOf(const AidlTypeSpecifier& aidl) {
-  return JavaSignatureOfInternal(aidl, true, true);
+  return JavaSignatureOfInternal(aidl, /*instantiable=*/true, /*omit_array=*/true,
+                                 /*boxing=*/false);
+}
+
+string JavaBoxingTypeOf(const AidlTypeSpecifier& aidl) {
+  AIDL_FATAL_IF(!AidlTypenames::IsPrimitiveTypename(aidl.GetName()), aidl);
+  return JavaSignatureOfInternal(aidl, /*instantiable=*/false, /*omit_array=*/false,
+                                 /*boxing=*/true);
 }
 
 string DefaultJavaValueOf(const AidlTypeSpecifier& aidl) {
@@ -209,14 +218,6 @@
   }
 }
 
-static string GetFlagFor(const CodeGeneratorContext& c) {
-  if (c.is_return_value) {
-    return "android.os.Parcelable.PARCELABLE_WRITE_RETURN_VALUE";
-  } else {
-    return "0";
-  }
-}
-
 typedef void (*ParcelHelperGenerator)(CodeWriter&, const Options&);
 
 static void GenerateTypedObjectHelper(CodeWriter& out, const Options&) {
@@ -408,9 +409,8 @@
                c.parcel,
                "v",
                c.min_sdk_version,
-               c.is_return_value,
+               c.write_to_parcel_flag,
                c.is_classloader_created,
-               c.filename,
            };
            WriteToParcelFor(value_context);
            c.writer.Dedent();
@@ -441,15 +441,17 @@
       {"ParcelFileDescriptor",
        [](const CodeGeneratorContext& c) {
          if (c.min_sdk_version >= 23u) {
-           c.writer << c.parcel << ".writeTypedObject(" << c.var << ", " << GetFlagFor(c) << ");\n";
+           c.writer << c.parcel << ".writeTypedObject(" << c.var << ", " << c.write_to_parcel_flag
+                    << ");\n";
          } else {
            c.writer << "_Parcel.writeTypedObject(" << c.parcel << ", " << c.var << ", "
-                    << GetFlagFor(c) << ");\n";
+                    << c.write_to_parcel_flag << ");\n";
          }
        }},
       {"ParcelFileDescriptor[]",
        [](const CodeGeneratorContext& c) {
-         c.writer << c.parcel << ".writeTypedArray(" << c.var << ", " << GetFlagFor(c) << ");\n";
+         c.writer << c.parcel << ".writeTypedArray(" << c.var << ", " << c.write_to_parcel_flag
+                  << ");\n";
        }},
       {"CharSequence",
        [](const CodeGeneratorContext& c) {
@@ -459,7 +461,7 @@
          c.writer.Indent();
          c.writer << c.parcel << ".writeInt(1);\n";
          c.writer << "android.text.TextUtils.writeToParcel(" << c.var << ", " << c.parcel << ", "
-                  << GetFlagFor(c) << ");\n";
+                  << c.write_to_parcel_flag << ");\n";
          c.writer.Dedent();
          c.writer << "}\n";
          c.writer << "else {\n";
@@ -478,7 +480,7 @@
   if (found != method_map.end()) {
     found->second(c);
   } else if (c.type.IsFixedSizeArray()) {
-    std::vector<std::string> args = {c.var, GetFlagFor(c)};
+    std::vector<std::string> args = {c.var, c.write_to_parcel_flag};
     for (auto dim : c.type.GetFixedSizeArrayDimensions()) {
       args.push_back(std::to_string(dim));
     }
@@ -494,13 +496,15 @@
       }
     } else if (t->AsParcelable() != nullptr) {
       if (c.type.IsArray()) {
-        c.writer << c.parcel << ".writeTypedArray(" << c.var << ", " << GetFlagFor(c) << ");\n";
+        c.writer << c.parcel << ".writeTypedArray(" << c.var << ", " << c.write_to_parcel_flag
+                 << ");\n";
       } else {
         if (c.min_sdk_version >= 23u) {
-          c.writer << c.parcel << ".writeTypedObject(" << c.var << ", " << GetFlagFor(c) << ");\n";
+          c.writer << c.parcel << ".writeTypedObject(" << c.var << ", " << c.write_to_parcel_flag
+                   << ");\n";
         } else {
           c.writer << "_Parcel.writeTypedObject(" << c.parcel << ", " << c.var << ", "
-                   << GetFlagFor(c) << ");\n";
+                   << c.write_to_parcel_flag << ");\n";
         }
       }
     }
@@ -634,9 +638,8 @@
                c.parcel,
                "v",
                c.min_sdk_version,
-               c.is_return_value,
+               c.write_to_parcel_flag,
                c.is_classloader_created,
-               c.filename,
            };
            CreateFromParcelFor(value_context);
            c.writer << c.var << ".put(k, v);\n";
@@ -822,9 +825,8 @@
                c.parcel,
                "v",
                c.min_sdk_version,
-               c.is_return_value,
+               c.write_to_parcel_flag,
                c.is_classloader_created,
-               c.filename,
            };
            CreateFromParcelFor(value_context);
            c.writer << c.var << ".put(k, v);\n";
@@ -848,7 +850,9 @@
        [](const CodeGeneratorContext& c) {
          c.writer << "if ((0!=" << c.parcel << ".readInt())) {\n";
          c.writer.Indent();
-         c.writer << c.var << " = " << "android.os.ParcelFileDescriptor.CREATOR.createFromParcel(" << c.parcel << ");\n";
+         c.writer << c.var << " = "
+                  << "android.os.ParcelFileDescriptor.CREATOR.createFromParcel(" << c.parcel
+                  << ");\n";
          c.writer.Dedent();
          c.writer << "}\n";
        }},
@@ -893,6 +897,17 @@
 }
 
 void ToStringFor(const CodeGeneratorContext& c) {
+  // Use derived toString() for enum type annotated with @JavaDerive(toString=true)
+  if (auto t = c.type.GetDefinedType();
+      t != nullptr && t->AsEnumDeclaration() && t->JavaDerive("toString")) {
+    if (c.type.IsArray()) {
+      c.writer << c.type.GetName() << ".$.arrayToString(" << c.var << ")";
+    } else {
+      c.writer << c.type.GetName() << ".$.toString(" << c.var << ")";
+    }
+    return;
+  }
+
   if (c.type.IsArray()) {
     if (c.type.IsDynamicArray() || c.type.GetFixedSizeArrayDimensions().size() == 1) {
       c.writer << "java.util.Arrays.toString(" << c.var << ")";
diff --git a/aidl_to_java.h b/aidl_to_java.h
index 5a4632c..34c3239 100644
--- a/aidl_to_java.h
+++ b/aidl_to_java.h
@@ -47,6 +47,10 @@
 // This includes generic type parameters with array modifiers.
 string JavaSignatureOf(const AidlTypeSpecifier& aidl);
 
+// Returns the Java boxing type of the AIDL type spec.
+// aidl type should be a primitive type.
+string JavaBoxingTypeOf(const AidlTypeSpecifier& aidl);
+
 // Returns the instantiable Jva type signature of the AIDL type spec
 // This includes generic type parameters, but excludes array modifiers.
 string InstantiableJavaSignatureOf(const AidlTypeSpecifier& aidl);
@@ -63,11 +67,11 @@
   const string parcel;
   const string var;
   const uint32_t min_sdk_version;
-  // Set to true when the marshalled data will be returned to the client
+  // Set PARCELABLE_WRITE_RETURN_VALUE when the marshalled data will be returned to the client.
   // This is given as a hint to the Parcelable that is being marshalled
   // so that the Parcelable can release its resource after the marshalling
   // is done.
-  const bool is_return_value;
+  const string write_to_parcel_flag;
 
   // Most of the variables created by AIDL compiler are typed, i.e., the code
   // knows exactly what type of object is in the parcel -- because the parcel
@@ -85,9 +89,6 @@
   // We emit the code at most once per an AIDL method, otherwise we are wasting
   // time doing the same thing multiple time.
   bool* const is_classloader_created;
-
-  // for error message printing
-  const string filename;
 };
 
 // Writes code fragment that writes a variable to the parcel.
diff --git a/aidl_unittest.cpp b/aidl_unittest.cpp
index 8908faf..ee0de54 100644
--- a/aidl_unittest.cpp
+++ b/aidl_unittest.cpp
@@ -322,16 +322,6 @@
   EXPECT_EQ(expected_stderr, GetCapturedStderr());
 }
 
-TEST_P(AidlTest, RejectsRepeatedAnnotations) {
-  const string method = R"(@Hide @Hide parcelable Foo {})";
-  const string expected_stderr =
-      "ERROR: Foo.aidl:1.23-27: 'Hide' is repeated, but not allowed. Previous location: "
-      "Foo.aidl:1.1-6\n";
-  CaptureStderr();
-  EXPECT_EQ(nullptr, Parse("Foo.aidl", method, typenames_, GetLanguage()));
-  EXPECT_EQ(expected_stderr, GetCapturedStderr());
-}
-
 TEST_P(AidlTest, AcceptsEmptyParcelable) {
   CaptureStderr();
   EXPECT_NE(nullptr, Parse("Foo.aidl", "parcelable Foo {}", typenames_, GetLanguage()));
@@ -630,14 +620,6 @@
     EXPECT_FALSE(compile_aidl(java_options, io_delegate_));
     EXPECT_THAT(GetCapturedStderr(), HasSubstr("@JavaDerive is not available."));
   }
-
-  {
-    io_delegate_.SetFileContents("a/IFoo.aidl", "package a; @JavaDerive enum IFoo { A=1, }");
-    Options java_options = Options::From("aidl --lang=java -o out a/IFoo.aidl");
-    CaptureStderr();
-    EXPECT_FALSE(compile_aidl(java_options, io_delegate_));
-    EXPECT_THAT(GetCapturedStderr(), HasSubstr("@JavaDerive is not available."));
-  }
 }
 
 TEST_P(AidlTest, ParseDescriptorAnnotation) {
@@ -681,7 +663,8 @@
 
 TEST_P(AidlTest, AnnotationsInMultiplePlaces) {
   const string oneway_method =
-      "package a; interface IFoo { @UnsupportedAppUsage oneway @Hide void f(int a); }";
+      "package a; interface IFoo { @UnsupportedAppUsage oneway @PropagateAllowBlocking void f(int "
+      "a); }";
   const AidlDefinedType* defined = Parse("a/IFoo.aidl", oneway_method, typenames_, GetLanguage());
   ASSERT_NE(nullptr, defined);
   const AidlInterface* iface = defined->AsInterface();
@@ -694,7 +677,7 @@
 
   // TODO(b/151102494): these annotations should be on the method
   ASSERT_NE(nullptr, ret_type.UnsupportedAppUsage());
-  ASSERT_TRUE(ret_type.IsHide());
+  ASSERT_TRUE(ret_type.IsPropagateAllowBlocking());
 }
 
 TEST_P(AidlTest, AnnotationValueAttribute) {
@@ -1155,6 +1138,19 @@
                       {Options::Language::NDK, {"out/aidl/Foo.h", "__attribute__((deprecated"}},
                       {Options::Language::RUST, {"out/Foo.rs", "#[deprecated"}},
                   });
+
+  CheckDeprecated("Foo.aidl",
+                  "enum Foo {\n"
+                  " /** @deprecated */\n"
+                  " FOO,\n"
+                  " BAR,\n"
+                  "}",
+                  {
+                      {Options::Language::JAVA, {"out/Foo.java", "@Deprecated"}},
+                      {Options::Language::CPP, {"out/Foo.h", "__attribute__((deprecated"}},
+                      {Options::Language::NDK, {"out/aidl/Foo.h", "__attribute__((deprecated"}},
+                      {Options::Language::RUST, {"out/Foo.rs", "#[deprecated"}},
+                  });
 }
 
 TEST_P(AidlTest, RequireOuterClass) {
@@ -1396,9 +1392,12 @@
 TEST_P(AidlTest, FailOnNoDefinedTypes) {
   AidlError error;
   const string expected_stderr = "ERROR: p/IFoo.aidl:1.11-11: syntax error, unexpected $end\n";
+  const string expected_stderr_newbison =
+      "ERROR: p/IFoo.aidl:1.11-11: syntax error, unexpected end of file\n";
   CaptureStderr();
   EXPECT_EQ(nullptr, Parse("p/IFoo.aidl", R"(package p;)", typenames_, GetLanguage(), &error));
-  EXPECT_EQ(expected_stderr, GetCapturedStderr());
+  EXPECT_THAT(GetCapturedStderr(),
+              testing::AnyOf(testing::Eq(expected_stderr), testing::Eq(expected_stderr_newbison)));
   EXPECT_EQ(AidlError::PARSE_ERROR, error);
 }
 
@@ -2609,9 +2608,12 @@
 TEST_P(AidlTest, FailParseOnEmptyFile) {
   const string contents = "";
   const string expected_stderr = "ERROR: a/IFoo.aidl:1.1-1: syntax error, unexpected $end\n";
+  const string expected_stderr_newbison =
+      "ERROR: a/IFoo.aidl:1.1-1: syntax error, unexpected end of file\n";
   CaptureStderr();
   EXPECT_EQ(nullptr, Parse("a/IFoo.aidl", contents, typenames_, GetLanguage()));
-  EXPECT_EQ(expected_stderr, GetCapturedStderr());
+  EXPECT_THAT(GetCapturedStderr(),
+              testing::AnyOf(testing::Eq(expected_stderr), testing::Eq(expected_stderr_newbison)));
 }
 
 TEST_F(AidlTest, MultipleInputFiles) {
@@ -5050,7 +5052,7 @@
 TEST_P(AidlTest, WarningInterfaceName) {
   io_delegate_.SetFileContents("p/Foo.aidl", "interface Foo {}");
   auto options = Options::From("aidl --lang " + to_string(GetLanguage()) +
-                               " -Weverything -o out -h out p/Foo.aidl");
+                               " -Winterface-name -o out -h out p/Foo.aidl");
   CaptureStderr();
   EXPECT_TRUE(compile_aidl(options, io_delegate_));
   EXPECT_EQ("WARNING: p/Foo.aidl:1.1-10: Interface names should start with I. [-Winterface-name]\n",
@@ -5060,7 +5062,7 @@
 TEST_P(AidlTest, ErrorInterfaceName) {
   io_delegate_.SetFileContents("p/Foo.aidl", "interface Foo {}");
   auto options = Options::From("aidl --lang " + to_string(GetLanguage()) +
-                               " -Weverything -Werror -o out -h out p/Foo.aidl");
+                               " -Winterface-name -Werror -o out -h out p/Foo.aidl");
   CaptureStderr();
   EXPECT_FALSE(compile_aidl(options, io_delegate_));
   EXPECT_EQ("ERROR: p/Foo.aidl:1.1-10: Interface names should start with I. [-Winterface-name]\n",
@@ -5117,17 +5119,6 @@
   }
 }
 
-TEST_F(AidlTest, HideIsNotForArgs) {
-  io_delegate_.SetFileContents("IFoo.aidl",
-                               "interface IFoo {\n"
-                               "  void foo(in @Hide int x);\n"
-                               "}");
-  auto options = Options::From("aidl --lang=java IFoo.aidl");
-  CaptureStderr();
-  EXPECT_FALSE(compile_aidl(options, io_delegate_));
-  EXPECT_THAT(GetCapturedStderr(), HasSubstr("@Hide is not available"));
-}
-
 TEST_F(AidlTest, SuppressWarningsIsNotForArgs) {
   io_delegate_.SetFileContents(
       "IFoo.aidl",
@@ -5168,24 +5159,6 @@
   EXPECT_EQ(GetCapturedStderr(), "");
 }
 
-TEST_F(AidlTest, PropagateBeforeTiramisu) {
-  io_delegate_.SetFileContents("p/IFoo.aidl",
-                               "interface IFoo{\n"
-                               "  @PropagateAllowBlocking IBinder foo();\n"
-                               "}");
-  CaptureStderr();
-  EXPECT_FALSE(compile_aidl(
-      Options::From("aidl --lang=java --min_sdk_version 30 -o out p/IFoo.aidl"), io_delegate_));
-  auto captured_stderr = GetCapturedStderr();
-  EXPECT_THAT(captured_stderr, HasSubstr("@PropagateAllowBlocking requires"));
-
-  CaptureStderr();
-  EXPECT_TRUE(
-      compile_aidl(Options::From("aidl --lang=java --min_sdk_version Tiramisu -o out p/IFoo.aidl"),
-                   io_delegate_));
-  EXPECT_EQ(GetCapturedStderr(), "");
-}
-
 TEST_F(AidlTest, RustNameOf_PfdFixedArray) {
   auto pfd = typenames_.MakeResolvedType(AIDL_LOCATION_HERE, "ParcelFileDescriptor", false);
   ASSERT_TRUE(pfd->MakeArray(FixedSizeArray{
diff --git a/build/Android.bp b/build/Android.bp
index 0609407..24b4a93 100644
--- a/build/Android.bp
+++ b/build/Android.bp
@@ -109,6 +109,43 @@
 }
 
 aidl_interface {
+    name: "test-piece-ndk-sdk-29",
+    local_include_dir: "tests_1",
+    flags: ["-Werror"],
+    srcs: [
+        "tests_1/some_package/IFoo.aidl",
+        "tests_1/some_package/Thing.aidl",
+        "tests_1/some_package/sub_package/*.aidl", // testing glob w/o filegroup
+    ],
+    unstable: true,
+    backend: { ndk: { sdk_version: "29", }, },
+}
+aidl_interface {
+    name: "test-piece-ndk-sdk-30",
+    local_include_dir: "tests_1",
+    flags: ["-Werror"],
+    srcs: [
+        "tests_1/some_package/IFoo.aidl",
+        "tests_1/some_package/Thing.aidl",
+        "tests_1/some_package/sub_package/*.aidl", // testing glob w/o filegroup
+    ],
+    unstable: true,
+    backend: { ndk: { sdk_version: "30", }, },
+}
+aidl_interface {
+    name: "test-piece-ndk-sdk-31",
+    local_include_dir: "tests_1",
+    flags: ["-Werror"],
+    srcs: [
+        "tests_1/some_package/IFoo.aidl",
+        "tests_1/some_package/Thing.aidl",
+        "tests_1/some_package/sub_package/*.aidl", // testing glob w/o filegroup
+    ],
+    unstable: true,
+    backend: { ndk: { sdk_version: "31", }, },
+}
+
+aidl_interface {
     name: "test-piece-2",
     local_include_dir: "tests_1",
     flags: ["-Werror"],
@@ -125,7 +162,13 @@
         },
     },
     gen_trace: true,
-    versions: ["1"],
+    versions_with_info: [
+        {
+            version: "1",
+            imports: ["test-piece-1-V3"],
+        },
+    ],
+
 }
 
 aidl_interface {
diff --git a/build/aidl_api.go b/build/aidl_api.go
index 62d2541..1fc4474 100644
--- a/build/aidl_api.go
+++ b/build/aidl_api.go
@@ -149,12 +149,133 @@
 	return apiDump{version, apiDir, apiFiles.Paths(), android.OptionalPathForPath(hashFile)}
 }
 
+func wrapWithDiffCheckIfElse(m *aidlApi, rb *android.RuleBuilder, writer func(*android.RuleBuilderCommand), elseBlock func(*android.RuleBuilderCommand)) {
+	rbc := rb.Command()
+	rbc.Text("if [ \"$(cat ").Input(m.hasDevelopment).Text(")\" = \"1\" ]; then")
+	writer(rbc)
+	rbc.Text("; else")
+	elseBlock(rbc)
+	rbc.Text("; fi")
+}
+
+func wrapWithDiffCheckIf(m *aidlApi, rb *android.RuleBuilder, writer func(*android.RuleBuilderCommand), needToWrap bool) {
+	rbc := rb.Command()
+	if needToWrap {
+		rbc.Text("if [ \"$(cat ").Input(m.hasDevelopment).Text(")\" = \"1\" ]; then")
+	}
+	writer(rbc)
+	if needToWrap {
+		rbc.Text("; fi")
+	}
+}
+
+// Migrate `versions` into `version_with_info`, and then append a version if it isn't nil
+func (m *aidlApi) migrateAndAppendVersion(ctx android.ModuleContext, rb *android.RuleBuilder, version *string, transitive bool) {
+	isFreezingApi := version != nil
+
+	// Remove `versions` property which is deprecated.
+	wrapWithDiffCheckIf(m, rb, func(rbc *android.RuleBuilderCommand) {
+		rbc.BuiltTool("bpmodify").
+			Text("-w -m " + m.properties.BaseName).
+			Text("-parameter versions -remove-property").
+			Text(android.PathForModuleSrc(ctx, "Android.bp").String())
+	}, isFreezingApi)
+
+	var iface *aidlInterface
+	ctx.VisitDirectDeps(func(dep android.Module) {
+		switch ctx.OtherModuleDependencyTag(dep).(type) {
+		case interfaceDepTag:
+			iface = dep.(*aidlInterface)
+		}
+	})
+	if iface == nil {
+		ctx.ModuleErrorf("aidl_interface %s doesn't exist", m.properties.BaseName)
+		return
+	}
+	var versions []string
+	if len(iface.properties.Versions_with_info) == 0 {
+		versions = append(versions, iface.getVersions()...)
+	}
+	if isFreezingApi {
+		versions = append(versions, *version)
+	}
+	for _, v := range versions {
+		importIfaces := make(map[string]*aidlInterface)
+		ctx.VisitDirectDeps(func(dep android.Module) {
+			if _, ok := ctx.OtherModuleDependencyTag(dep).(importInterfaceDepTag); ok {
+				other := dep.(*aidlInterface)
+				importIfaces[other.BaseModuleName()] = other
+			}
+		})
+		imports := make([]string, 0, len(iface.getImportsForVersion(v)))
+		needTransitiveFreeze := isFreezingApi && v == *version && transitive
+
+		if needTransitiveFreeze {
+			importApis := make(map[string]*aidlApi)
+			ctx.WalkDeps(func(child android.Module, parent android.Module) bool {
+				if api, ok := child.(*aidlApi); ok {
+					moduleName := strings.TrimSuffix(api.Name(), aidlApiSuffix)
+					if _, ok := importIfaces[moduleName]; ok {
+						importApis[moduleName] = api
+						return false
+					}
+				}
+				return true
+			})
+			wrapWithDiffCheckIf(m, rb, func(rbc *android.RuleBuilderCommand) {
+				rbc.BuiltTool("bpmodify").
+					Text("-w -m " + m.properties.BaseName).
+					Text("-parameter versions_with_info -add-literal '").
+					Text(fmt.Sprintf(`{version: "%s", imports: [`, v))
+
+				for _, im := range iface.getImportsForVersion(v) {
+					moduleName, version := parseModuleWithVersion(im)
+
+					// Invoke an imported interface's freeze-api only if it depends on ToT version explicitly or implicitly.
+					if version == importIfaces[moduleName].nextVersion() || !hasVersionSuffix(im) {
+						rb.Command().Text(fmt.Sprintf(`echo "Call %s-freeze-api because %s depends on %s."`, moduleName, m.properties.BaseName, moduleName))
+						rbc.Implicit(importApis[moduleName].freezeApiTimestamp)
+					}
+					if hasVersionSuffix(im) {
+						rbc.Text(fmt.Sprintf(`"%s",`, im))
+					} else {
+						rbc.Text("\"" + im + "-V'" + `$(if [ "$(cat `).
+							Input(importApis[im].hasDevelopment).
+							Text(`)" = "1" ]; then echo "` + importIfaces[im].nextVersion() +
+								`"; else echo "` + importIfaces[im].latestVersion() + `"; fi)'", `)
+					}
+				}
+				rbc.Text("]}' ").
+					Text(android.PathForModuleSrc(ctx, "Android.bp").String())
+			}, isFreezingApi)
+		} else {
+			for _, im := range iface.getImportsForVersion(v) {
+				if hasVersionSuffix(im) {
+					imports = append(imports, im)
+				} else {
+					imports = append(imports, im+"-V"+importIfaces[im].latestVersion())
+				}
+			}
+			importsStr := strings.Join(wrap(`"`, imports, `"`), ", ")
+			data := fmt.Sprintf(`{version: "%s", imports: [%s]}`, v, importsStr)
+
+			// Also modify Android.bp file to add the new version to the 'versions_with_info' property.
+			wrapWithDiffCheckIf(m, rb, func(rbc *android.RuleBuilderCommand) {
+				rbc.BuiltTool("bpmodify").
+					Text("-w -m " + m.properties.BaseName).
+					Text("-parameter versions_with_info -add-literal '" + data + "' ").
+					Text(android.PathForModuleSrc(ctx, "Android.bp").String())
+			}, isFreezingApi)
+		}
+	}
+}
+
 func (m *aidlApi) makeApiDumpAsVersion(ctx android.ModuleContext, dump apiDump, version string, latestVersionDump *apiDump) android.WritablePath {
 	creatingNewVersion := version != currentVersion
 	moduleDir := android.PathForModuleSrc(ctx).String()
 	targetDir := filepath.Join(moduleDir, m.apiDir(), version)
 	rb := android.NewRuleBuilder(pctx, ctx)
-
+	transitive := ctx.Config().IsEnvTrue("AIDL_TRANSITIVE_FREEZE")
 	var actionWord string
 	if creatingNewVersion {
 		actionWord = "Making"
@@ -163,27 +284,24 @@
 		// otherwise we will be unnecessarily creating many versions.
 		// Copy the given dump to the target directory only when the equality check failed
 		// (i.e. `has_development` file contains "1").
-		rb.Command().
-			Text("if [ \"$(cat ").Input(m.hasDevelopment).Text(")\" = \"1\" ]; then").
-			Text("mkdir -p " + targetDir + " && ").
-			Text("cp -rf " + dump.dir.String() + "/. " + targetDir).Implicits(dump.files).
-			Text("; fi")
-
-		// Also modify Android.bp file to add the new version to the 'versions' property.
-		rb.Command().
-			Text("if [ \"$(cat ").Input(m.hasDevelopment).Text(")\" = \"1\" ]; then").
-			BuiltTool("bpmodify").
-			Text("-w -m " + m.properties.BaseName).
-			Text("-parameter versions -a " + version).
-			Text(android.PathForModuleSrc(ctx, "Android.bp").String()).
-			Text("; fi")
-
+		wrapWithDiffCheckIf(m, rb, func(rbc *android.RuleBuilderCommand) {
+			rbc.Text("mkdir -p " + targetDir + " && ").
+				Text("cp -rf " + dump.dir.String() + "/. " + targetDir).Implicits(dump.files)
+		}, true /* needToWrap */)
+		wrapWithDiffCheckIfElse(m, rb, func(rbc *android.RuleBuilderCommand) {
+			rbc.Text(fmt.Sprintf(`echo "There is change between ToT version and the latest stable version. Freezing %s-V%s."`, m.properties.BaseName, version))
+		}, func(rbc *android.RuleBuilderCommand) {
+			rbc.Text(fmt.Sprintf(`echo "There is no change from the latest stable version of %s. Nothing happened."`, m.properties.BaseName))
+		})
+		m.migrateAndAppendVersion(ctx, rb, &version, transitive)
 	} else {
 		actionWord = "Updating"
 		// We are updating the current version. Don't copy .hash to the current dump
 		rb.Command().Text("mkdir -p " + targetDir)
 		rb.Command().Text("rm -rf " + targetDir + "/*")
 		rb.Command().Text("cp -rf " + dump.dir.String() + "/* " + targetDir).Implicits(dump.files)
+
+		m.migrateAndAppendVersion(ctx, rb, nil, false)
 	}
 
 	timestampFile := android.PathForModuleOut(ctx, "updateapi_"+version+".timestamp")
@@ -460,7 +578,7 @@
 		AidlRoot:  aidlRoot,
 		Stability: i.properties.Stability,
 		Imports:   i.properties.Imports,
-		Versions:  i.properties.Versions,
+		Versions:  i.getVersions(),
 		Dumpapi:   i.properties.Dumpapi,
 	})
 	return apiModule
diff --git a/build/aidl_interface.go b/build/aidl_interface.go
index 8d27826..36527e4 100644
--- a/build/aidl_interface.go
+++ b/build/aidl_interface.go
@@ -327,9 +327,20 @@
 	// interface must be kept stable as long as it is used.
 	Stability *string
 
+	// Deprecated: Use `versions_with_info` instead. Don't use `versions` property directly.
+	Versions []string
+
 	// Previous API versions that are now frozen. The version that is last in
 	// the list is considered as the most recent version.
-	Versions []string
+	// The struct contains both version and imports information per a version.
+	// Until versions property is removed, don't use `versions_with_info` directly.
+	Versions_with_info []struct {
+		Version string
+		Imports []string
+	}
+
+	// Use aidlInterface.getVersions()
+	VersionsInternal []string `blueprint:"mutated"`
 
 	// The minimum version of the sdk that the compiled artifacts will run against
 	// For native modules, the property needs to be set when a module is a part of mainline modules(APEX).
@@ -369,6 +380,11 @@
 		Ndk struct {
 			CommonNativeBackendProperties
 
+			// Set to the version of the sdk to compile against, for the NDK
+			// variant.
+			// Default: current
+			Sdk_version *string
+
 			// If set to false, the ndk backend is exclusive to platform and is not
 			// available to applications. Default is true (i.e. available to both
 			// applications and platform).
@@ -559,7 +575,7 @@
 			anImportWithVersion := tag.anImport
 			_, version := parseModuleWithVersion(tag.anImport)
 			if version != "" {
-				candidateVersions := concat(other.properties.Versions, []string{other.nextVersion()})
+				candidateVersions := concat(other.getVersions(), []string{other.nextVersion()})
 				if !android.InList(version, candidateVersions) {
 					mctx.PropertyErrorf("imports", "%q depends on %q version %q(%q), which doesn't exist. The version must be one of %q", i.ModuleBase.Name(), anImport, version, anImportWithVersion, candidateVersions)
 				}
@@ -613,9 +629,29 @@
 	}
 }
 func (i *aidlInterface) checkVersions(mctx android.LoadHookContext) {
+	if len(i.properties.Versions) > 0 && len(i.properties.Versions_with_info) > 0 {
+		mctx.ModuleErrorf("versions:%q and versions_with_info:%q cannot be used at the same time. Use versions_with_info instead of versions.", i.properties.Versions, i.properties.Versions_with_info)
+	}
+
+	if len(i.properties.Versions) > 0 {
+		i.properties.VersionsInternal = make([]string, len(i.properties.Versions))
+		copy(i.properties.VersionsInternal, i.properties.Versions)
+	} else if len(i.properties.Versions_with_info) > 0 {
+		i.properties.VersionsInternal = make([]string, len(i.properties.Versions_with_info))
+		for idx, value := range i.properties.Versions_with_info {
+			i.properties.VersionsInternal[idx] = value.Version
+			for _, im := range value.Imports {
+				if !hasVersionSuffix(im) {
+					mctx.ModuleErrorf("imports in versions_with_info must specify its version, but %s. Add a version suffix(such as %s-V1).", im, im)
+					return
+				}
+			}
+		}
+	}
+
 	versions := make(map[string]bool)
-	intVersions := make([]int, 0, len(i.properties.Versions))
-	for _, ver := range i.properties.Versions {
+	intVersions := make([]int, 0, len(i.getVersions()))
+	for _, ver := range i.getVersions() {
 		if _, dup := versions[ver]; dup {
 			mctx.PropertyErrorf("versions", "duplicate found", ver)
 			continue
@@ -634,7 +670,7 @@
 
 	}
 	if !mctx.Failed() && !sort.IntsAreSorted(intVersions) {
-		mctx.PropertyErrorf("versions", "should be sorted, but is %v", i.properties.Versions)
+		mctx.PropertyErrorf("versions", "should be sorted, but is %v", i.getVersions())
 	}
 }
 func (i *aidlInterface) checkVndkUseVersion(mctx android.LoadHookContext) {
@@ -648,7 +684,7 @@
 	if *i.properties.Vndk_use_version == i.nextVersion() {
 		return
 	}
-	for _, ver := range i.properties.Versions {
+	for _, ver := range i.getVersions() {
 		if *i.properties.Vndk_use_version == ver {
 			return
 		}
@@ -660,7 +696,7 @@
 	if proptools.Bool(i.properties.Unstable) {
 		return ""
 	}
-	return nextVersion(i.properties.Versions)
+	return nextVersion(i.getVersions())
 }
 
 func nextVersion(versions []string) string {
@@ -679,11 +715,15 @@
 	if !i.hasVersion() {
 		return "0"
 	}
-	return i.properties.Versions[len(i.properties.Versions)-1]
+	return i.getVersions()[len(i.getVersions())-1]
 }
 
 func (i *aidlInterface) hasVersion() bool {
-	return len(i.properties.Versions) > 0
+	return len(i.getVersions()) > 0
+}
+
+func (i *aidlInterface) getVersions() []string {
+	return i.properties.VersionsInternal
 }
 
 func hasVersionSuffix(moduleName string) bool {
@@ -759,7 +799,7 @@
 		mctx.PropertyErrorf("versions", "must be set (need to be frozen) when \"unstable\" is false, PLATFORM_VERSION_CODENAME is REL, and \"owner\" property is missing.")
 	}
 
-	versions := i.properties.Versions
+	versions := i.getVersions()
 	nextVersion := i.nextVersion()
 	shouldGenerateLangBackendMap := map[string]bool{
 		langCpp:  i.shouldGenerateCppBackend(),
@@ -836,7 +876,7 @@
 
 	i.preprocessed = make(map[string]android.WritablePath)
 	// generate (len(versions) + 1) preprocessed.aidl files
-	for _, version := range concat(i.properties.Versions, []string{i.nextVersion()}) {
+	for _, version := range concat(i.getVersions(), []string{i.nextVersion()}) {
 		i.preprocessed[version] = i.buildPreprocessed(ctx, version)
 	}
 	// helpful aliases
@@ -851,18 +891,27 @@
 	}
 }
 
-// imported interfaces
-// TODO(b/146436251) use imports in versions_with_info
-// For example, foo-V1 should use bar-V1 while foo-V2 should use bar-V2
-//   name: "foo",
-//   versions_with_info: [
-//     { version: "1", imports: ["bar-V1"]},
-//     { version: "2", imports: ["bar-V2"]},
-//   ]
+func (i *aidlInterface) getImportsForVersion(version string) []string {
+	// `Imports` is used when version == i.nextVersion() or`versions` is defined instead of `versions_with_info`
+	importsSrc := i.properties.Imports
+	for _, v := range i.properties.Versions_with_info {
+		if v.Version == version {
+			importsSrc = v.Imports
+			break
+		}
+	}
+	imports := make([]string, len(importsSrc))
+	copy(imports, importsSrc)
+
+	return imports
+}
+
 func (i *aidlInterface) getImports(version string) map[string]string {
 	imports := make(map[string]string)
+	imports_src := i.getImportsForVersion(version)
+
 	useLatestStable := !proptools.Bool(i.properties.Unstable) && version != "" && version != i.nextVersion()
-	for _, importString := range i.properties.Imports {
+	for _, importString := range imports_src {
 		name, targetVersion := parseModuleWithVersion(importString)
 		if targetVersion == "" && useLatestStable {
 			targetVersion = "latest"
diff --git a/build/aidl_interface_backends.go b/build/aidl_interface_backends.go
index ac4fce4..62a167c 100644
--- a/build/aidl_interface_backends.go
+++ b/build/aidl_interface_backends.go
@@ -75,13 +75,13 @@
 	}, &aidlGenProperties{
 		Srcs:            srcs,
 		AidlRoot:        aidlRoot,
-		Imports:         i.properties.Imports,
+		Imports:         i.getImportsForVersion(version),
 		Stability:       i.properties.Stability,
 		Min_sdk_version: i.minSdkVersion(lang),
 		Lang:            lang,
 		BaseName:        i.ModuleBase.Name(),
 		GenLog:          genLog,
-		Version:         i.versionForAidlGenRule(version),
+		Version:         i.versionForInitVersionCompat(version),
 		GenTrace:        genTrace,
 		Unstable:        i.properties.Unstable,
 		NotFrozen:       notFrozen,
@@ -122,7 +122,11 @@
 		targetProp.Product = nonAppProps
 		hostSupported = i.properties.Host_supported
 		if lang == langNdk && i.shouldGenerateAppNdkBackend() {
-			sdkVersion = proptools.StringPtr("current")
+			sdkVersion = i.properties.Backend.Ndk.Sdk_version
+			if sdkVersion == nil {
+				sdkVersion = proptools.StringPtr("current")
+			}
+
 			// Don't worry! This maps to libc++.so for the platform variant.
 			stl = proptools.StringPtr("c++_shared")
 		}
@@ -156,7 +160,7 @@
 		Lang:              lang,
 		AidlInterfaceName: i.ModuleBase.Name(),
 		Version:           version,
-		Imports:           i.properties.Imports,
+		Imports:           i.getImportsForVersion(version),
 		ModuleProperties: []interface{}{
 			&ccProperties{
 				Name:                      proptools.StringPtr(cppModuleGen),
@@ -219,13 +223,13 @@
 	}, &aidlGenProperties{
 		Srcs:            srcs,
 		AidlRoot:        aidlRoot,
-		Imports:         i.properties.Imports,
+		Imports:         i.getImportsForVersion(version),
 		Stability:       i.properties.Stability,
 		Min_sdk_version: minSdkVersion,
 		Platform_apis:   proptools.Bool(i.properties.Backend.Java.Platform_apis),
 		Lang:            langJava,
 		BaseName:        i.ModuleBase.Name(),
-		Version:         i.versionForAidlGenRule(version),
+		Version:         version,
 		GenRpc:          proptools.Bool(i.properties.Backend.Java.Gen_rpc),
 		GenTrace:        proptools.Bool(i.properties.Gen_trace),
 		Unstable:        i.properties.Unstable,
@@ -239,7 +243,7 @@
 		Lang:              langJava,
 		AidlInterfaceName: i.ModuleBase.Name(),
 		Version:           version,
-		Imports:           i.properties.Imports,
+		Imports:           i.getImportsForVersion(version),
 		ModuleProperties: []interface{}{&javaProperties{
 			Name:            proptools.StringPtr(javaModuleGen),
 			Installable:     proptools.BoolPtr(true),
@@ -271,12 +275,12 @@
 	}, &aidlGenProperties{
 		Srcs:            srcs,
 		AidlRoot:        aidlRoot,
-		Imports:         i.properties.Imports,
+		Imports:         i.getImportsForVersion(version),
 		Stability:       i.properties.Stability,
 		Min_sdk_version: i.minSdkVersion(langRust),
 		Lang:            langRust,
 		BaseName:        i.ModuleBase.Name(),
-		Version:         i.versionForAidlGenRule(version),
+		Version:         i.versionForInitVersionCompat(version),
 		Unstable:        i.properties.Unstable,
 		NotFrozen:       notFrozen,
 		Flags:           i.flagsForAidlGenRule(version),
@@ -299,7 +303,7 @@
 		Source_stem: proptools.StringPtr(versionedRustName),
 	}, &aidlRustSourceProviderProperties{
 		SourceGen:         rustSourceGen,
-		Imports:           i.properties.Imports,
+		Imports:           i.getImportsForVersion(version),
 		Version:           version,
 		AidlInterfaceName: i.ModuleBase.Name(),
 	})
@@ -339,7 +343,10 @@
 	}
 }
 
-func (i *aidlInterface) versionForAidlGenRule(version string) string {
+// For certain backend, avoid a difference between the initial version of a versioned
+// interface and an unversioned interface. This ensures that prebuilts can't prevent
+// an interface from switching from unversioned to versioned.
+func (i *aidlInterface) versionForInitVersionCompat(version string) string {
 	if !i.hasVersion() {
 		return ""
 	}
@@ -350,7 +357,7 @@
 	flags = append(flags, i.properties.Flags...)
 	// For ToT, turn on "-Weverything" (enable all warnings)
 	if version == i.nextVersion() {
-		flags = append(flags, "-Weverything")
+		flags = append(flags, "-Weverything -Wno-missing-permission-annotation")
 	}
 	return
 }
diff --git a/build/aidl_interface_metadata_singleton.go b/build/aidl_interface_metadata_singleton.go
index 9e2e014..8b421a5 100644
--- a/build/aidl_interface_metadata_singleton.go
+++ b/build/aidl_interface_metadata_singleton.go
@@ -99,7 +99,7 @@
 			info := moduleInfos[t.ModuleBase.Name()]
 			info.Stability = proptools.StringDefault(t.properties.Stability, "")
 			info.ComputedTypes = t.computedTypes
-			info.Versions = t.properties.Versions
+			info.Versions = t.getVersions()
 			moduleInfos[t.ModuleBase.Name()] = info
 		case *aidlGenRule:
 			info := moduleInfos[t.properties.BaseName]
diff --git a/build/aidl_test.go b/build/aidl_test.go
index 2d5dc17..8c258db 100644
--- a/build/aidl_test.go
+++ b/build/aidl_test.go
@@ -60,6 +60,12 @@
 	})
 }
 
+func setTransitiveFreezeEnv() android.FixturePreparer {
+	return android.FixtureMergeEnv(map[string]string{
+		"AIDL_TRANSITIVE_FREEZE": "true",
+	})
+}
+
 func _testAidl(t *testing.T, bp string, customizers ...android.FixturePreparer) android.FixturePreparer {
 	t.Helper()
 
@@ -1650,3 +1656,152 @@
 	testAidl(t, ``, files, setTestFreezeEnv())
 	testAidl(t, ``, files)
 }
+
+func TestVersionsWithInfoAndVersions(t *testing.T) {
+	conflictingFields := `
+	aidl_interface {
+		name: "foo",
+		versions: [
+			"1",
+		],
+		versions_with_info: [
+			{
+				version: "1",
+			}
+		],
+	}
+	`
+	files := withFiles(map[string][]byte{
+		"aidl_api/foo/1/foo.1.aidl": nil,
+		"aidl_api/foo/1/.hash":      nil,
+	})
+
+	expectedError := `Use versions_with_info instead of versions.`
+	testAidlError(t, expectedError, conflictingFields, files)
+}
+
+func TestVersionsWithInfo(t *testing.T) {
+	ctx, _ := testAidl(t, ``, withFiles(map[string][]byte{
+		"common/Android.bp": []byte(`
+		  aidl_interface {
+				name: "common",
+				srcs: ["ICommon.aidl"],
+				versions: ["1", "2"],
+			}
+		`),
+		"common/aidl_api/common/1/ICommon.aidl": nil,
+		"common/aidl_api/common/1/.hash":        nil,
+		"common/aidl_api/common/2/ICommon.aidl": nil,
+		"common/aidl_api/common/2/.hash":        nil,
+		"foo/Android.bp": []byte(`
+			aidl_interface {
+				name: "foo",
+				srcs: ["IFoo.aidl"],
+				imports: ["common"],
+				versions_with_info: [
+					{version: "1", imports: ["common-V1"]},
+					{version: "2", imports: ["common-V2"]},
+				]
+			}
+		`),
+		"foo/aidl_api/foo/1/IFoo.aidl": nil,
+		"foo/aidl_api/foo/1/.hash":     nil,
+		"foo/aidl_api/foo/2/IFoo.aidl": nil,
+		"foo/aidl_api/foo/2/.hash":     nil,
+	}))
+
+	fooV1Java := FindModule(ctx, "foo-V1-java", "android_common", "foo").(*java.Library)
+	android.AssertStringListContains(t, "a/foo-v1 deps", fooV1Java.CompilerDeps(), "common-V1-java")
+
+	fooV2Java := FindModule(ctx, "foo-V2-java", "android_common", "foo").(*java.Library)
+	android.AssertStringListContains(t, "a/foo-v2 deps", fooV2Java.CompilerDeps(), "common-V2-java")
+
+	fooV3Java := FindModule(ctx, "foo-V3-java", "android_common", "foo").(*java.Library)
+	android.AssertStringListContains(t, "a/foo-v3 deps", fooV3Java.CompilerDeps(), "common-V3-java")
+}
+
+func TestVersionsWithInfoImport(t *testing.T) {
+	testAidlError(t, "imports in versions_with_info must specify its version", ``, withFiles(map[string][]byte{
+		"common/Android.bp": []byte(`
+		  aidl_interface {
+				name: "common",
+				srcs: ["ICommon.aidl"],
+				versions: ["1", "2"],
+			}
+		`),
+		"common/aidl_api/common/1/ICommon.aidl": nil,
+		"common/aidl_api/common/1/.hash":        nil,
+		"common/aidl_api/common/2/ICommon.aidl": nil,
+		"common/aidl_api/common/2/.hash":        nil,
+		"foo/Android.bp": []byte(`
+			aidl_interface {
+				name: "foo",
+				srcs: ["IFoo.aidl"],
+				imports: ["common"],
+				versions_with_info: [
+					{version: "1", imports: ["common"]},
+					{version: "2", imports: ["common-V2"]},
+				]
+			}
+		`),
+		"foo/aidl_api/foo/1/IFoo.aidl": nil,
+		"foo/aidl_api/foo/1/.hash":     nil,
+		"foo/aidl_api/foo/2/IFoo.aidl": nil,
+		"foo/aidl_api/foo/2/.hash":     nil,
+	}))
+}
+
+func TestFreezeApiDeps(t *testing.T) {
+	for _, transitive := range []bool{true, false} {
+		for _, testcase := range []struct {
+			string
+			bool
+		}{{"common", true}, {"common-V3", true}, {"common-V2", false}} {
+			im := testcase.string
+			customizers := []android.FixturePreparer{
+				withFiles(map[string][]byte{
+					"common/Android.bp": []byte(`
+				  aidl_interface {
+						name: "common",
+						srcs: ["ICommon.aidl"],
+						versions: ["1", "2"],
+					}
+				`),
+					"common/aidl_api/common/1/ICommon.aidl": nil,
+					"common/aidl_api/common/1/.hash":        nil,
+					"common/aidl_api/common/2/ICommon.aidl": nil,
+					"common/aidl_api/common/2/.hash":        nil,
+					"foo/Android.bp": []byte(fmt.Sprintf(`
+					aidl_interface {
+						name: "foo",
+						srcs: ["IFoo.aidl"],
+						imports: ["%s"],
+						versions_with_info: [
+							{version: "1", imports: ["common-V1"]},
+							{version: "2", imports: ["common-V2"]},
+						]
+					}
+				`, im)),
+					"foo/aidl_api/foo/1/IFoo.aidl": nil,
+					"foo/aidl_api/foo/1/.hash":     nil,
+					"foo/aidl_api/foo/2/IFoo.aidl": nil,
+					"foo/aidl_api/foo/2/.hash":     nil,
+				}),
+			}
+			if transitive {
+				customizers = append(customizers, setTransitiveFreezeEnv())
+			}
+
+			ctx, _ := testAidl(t, ``, customizers...)
+			shouldHaveDep := transitive && testcase.bool
+			fooFreezeApiRule := ctx.ModuleForTests("foo-api", "").Output("updateapi_3.timestamp")
+			commonFreezeApiOutput := ctx.ModuleForTests("common-api", "").Output("updateapi_3.timestamp").Output.String()
+			testMethod := android.AssertStringListDoesNotContain
+			if shouldHaveDep {
+				testMethod = android.AssertStringListContains
+			}
+			testMethod(t, "Only if AIDL_TRANSITIVE_FREEZE is set and an aidl_interface depends on an another aidl_interface's ToT version, an imported aidl_interface should be frozen as well.",
+				fooFreezeApiRule.Implicits.Strings(), commonFreezeApiOutput)
+		}
+	}
+}
diff --git a/check_valid.cpp b/check_valid.cpp
index 2d7fb90..e7bcb8d 100644
--- a/check_valid.cpp
+++ b/check_valid.cpp
@@ -80,16 +80,6 @@
     return true;
   });
 
-  v.Check([&](const AidlTypeSpecifier& type) {
-    // TODO(b/151102494): annotation is applied on the return type
-    if (type.IsPropagateAllowBlocking() && options.GetMinSdkVersion() < JAVA_PROPAGATE_VERSION) {
-      AIDL_ERROR(type) << "@PropagateAllowBlocking requires " << JAVA_PROPAGATE_VERSION
-                       << ". Current min_sdk_version is " << min_sdk_version << ".";
-      return false;
-    }
-    return true;
-  });
-
   VisitTopDown(v, doc);
   return v.success;
 }
diff --git a/diagnostics.cpp b/diagnostics.cpp
index 13f918f..0eca8ae 100644
--- a/diagnostics.cpp
+++ b/diagnostics.cpp
@@ -55,6 +55,9 @@
   DiagnosticsContext(DiagnosticMapping mapping) : mapping_({std::move(mapping)}) {}
   AidlErrorLog Report(const AidlLocation& loc, DiagnosticID id,
                       DiagnosticSeverity force_severity = DiagnosticSeverity::DISABLED) {
+    if (loc.IsInternal()) {
+      return AidlErrorLog(AidlErrorLog::NO_OP, loc);
+    }
     const std::string suffix = " [-W" + to_string(id) + "]";
     auto severity = std::max(force_severity, mapping_.top().Severity(id));
     switch (severity) {
@@ -296,6 +299,44 @@
   }
 };
 
+struct DiagnosePermissionAnnotations : DiagnosticsVisitor {
+  DiagnosePermissionAnnotations(DiagnosticsContext& diag) : DiagnosticsVisitor(diag) {}
+  void Visit(const AidlInterface& intf) override {
+    const std::string diag_message =
+        " is not annotated for permissions. Declare which permissions are "
+        "required using @EnforcePermission. If permissions are manually "
+        "verified within the implementation, use @PermissionManuallyEnforced. "
+        "If no permissions are required, use @RequiresNoPermission.";
+    if (intf.EnforceExpression() || intf.IsPermissionManual() || intf.IsPermissionNone()) {
+      return;
+    }
+    const auto& methods = intf.GetMethods();
+    std::vector<size_t> methods_without_annotations;
+    size_t num_user_defined_methods = 0;
+    for (size_t i = 0; i < methods.size(); ++i) {
+      auto& m = methods[i];
+      if (!m->IsUserDefined()) continue;
+      num_user_defined_methods++;
+      if (m->GetType().EnforceExpression() || m->GetType().IsPermissionManual() ||
+          m->GetType().IsPermissionNone()) {
+        continue;
+      }
+      methods_without_annotations.push_back(i);
+    }
+    if (methods_without_annotations.size() == num_user_defined_methods) {
+      diag.Report(intf.GetLocation(), DiagnosticID::missing_permission_annotation)
+          << intf.GetName() << diag_message
+          << " This can be done for the whole interface or for each method.";
+    } else {
+      for (size_t i : methods_without_annotations) {
+        auto& m = methods[i];
+        diag.Report(m->GetLocation(), DiagnosticID::missing_permission_annotation)
+            << m->GetName() << diag_message;
+      }
+    }
+  }
+};
+
 bool Diagnose(const AidlDocument& doc, const DiagnosticMapping& mapping) {
   DiagnosticsContext diag(mapping);
 
@@ -309,6 +350,7 @@
   DiagnoseOutNullable{diag}.Check(doc);
   DiagnoseImports{diag}.Check(doc);
   DiagnoseUntypedCollection{diag}.Check(doc);
+  DiagnosePermissionAnnotations{diag}.Check(doc);
 
   return diag.ErrorCount() == 0;
 }
diff --git a/diagnostics.inc b/diagnostics.inc
index 101b737..6a0570a 100644
--- a/diagnostics.inc
+++ b/diagnostics.inc
@@ -4,6 +4,7 @@
 DIAG(file_descriptor, "file-descriptor", false)
 DIAG(inout_parameter, "inout-parameter", false)
 DIAG(interface_name, "interface-name", false)
+DIAG(missing_permission_annotation, "missing-permission-annotation", false)
 DIAG(mixed_oneway, "mixed-oneway", false)
 DIAG(out_array, "out-array", false)
 DIAG(out_nullable, "out-nullable", false)
diff --git a/diagnostics_unittest.cpp b/diagnostics_unittest.cpp
index 0066b9f..3a45d09 100644
--- a/diagnostics_unittest.cpp
+++ b/diagnostics_unittest.cpp
@@ -24,7 +24,6 @@
 #include "parser.h"
 #include "tests/fake_io_delegate.h"
 
-using android::aidl::AidlError;
 using android::aidl::AidlTypenames;
 using android::aidl::DiagnosticID;
 using android::aidl::Options;
@@ -40,56 +39,63 @@
     for (const auto& [file, contents] : files) {
       io.SetFileContents(file, contents);
     }
+    if (!enable_diagnostic) {
+      ASSERT_TRUE(expect_diagnostic);
+      enable_diagnostic = expect_diagnostic;
+    }
     // emit diagnostics as warnings.
     // "java" has no specific meaning here because we're testing CheckValid()
-    const Options options =
-        Options::From("aidl " + optional_args + " -I . --lang java -o out -Weverything " + main);
+    const Options options = Options::From("aidl " + optional_args + " -I . --lang java -o out -W" +
+                                          to_string(*enable_diagnostic) + " " + main);
     CaptureStderr();
     load_and_validate_aidl(main, options, io, &typenames, nullptr);
     const std::string err = GetCapturedStderr();
-    if (expect_diagnostics.empty()) {
-      EXPECT_EQ("", err);
+    if (expect_diagnostic) {
+      EXPECT_THAT(err, testing::HasSubstr("-W" + to_string(*expect_diagnostic)));
     } else {
-      for (const auto id : expect_diagnostics) {
-        EXPECT_THAT(err, testing::HasSubstr("-W" + to_string(id)));
-      }
+      EXPECT_EQ("", err);
     }
   }
 
   AidlTypenames typenames;
   FakeIoDelegate io;
   std::string optional_args;
-  std::vector<DiagnosticID> expect_diagnostics;
+  // The type of diagnostic to enable for the test. If expect_diagnostic is
+  // set, use the same value.
+  std::optional<DiagnosticID> enable_diagnostic;
+  // The expected diagnostic. Must be set.
+  std::optional<DiagnosticID> expect_diagnostic;
 };
 
 TEST_F(DiagnosticsTest, const_name_ForEnumerator) {
-  expect_diagnostics = {DiagnosticID::const_name};
+  expect_diagnostic = DiagnosticID::const_name;
   ParseFiles({{"Foo.aidl", "enum Foo { foo }"}});
 }
 
 TEST_F(DiagnosticsTest, const_name_ForConstants) {
-  expect_diagnostics = {DiagnosticID::const_name};
+  expect_diagnostic = DiagnosticID::const_name;
   ParseFiles({{"IFoo.aidl", "interface IFoo { const int foo = 1; }"}});
 }
 
 TEST_F(DiagnosticsTest, interface_name) {
-  expect_diagnostics = {DiagnosticID::interface_name};
+  expect_diagnostic = DiagnosticID::interface_name;
   ParseFiles({{"Foo.aidl", "interface Foo { }"}});
 }
 
 TEST_F(DiagnosticsTest, enum_explicit_default) {
-  expect_diagnostics = {DiagnosticID::enum_explicit_default};
+  expect_diagnostic = DiagnosticID::enum_explicit_default;
   ParseFiles({{"Foo.aidl", "parcelable Foo { E e; }"}, {"E.aidl", "enum E { A }"}});
 }
 
 TEST_F(DiagnosticsTest, inout_parameter) {
-  expect_diagnostics = {DiagnosticID::inout_parameter};
+  expect_diagnostic = DiagnosticID::inout_parameter;
   ParseFiles({{"IFoo.aidl", "interface IFoo { void foo(inout Bar bar); }"},
               {"Bar.aidl", "parcelable Bar {}"}});
 }
 
 TEST_F(DiagnosticsTest, inout_parameter_SuppressAtMethodLevel) {
-  expect_diagnostics = {};
+  enable_diagnostic = DiagnosticID::inout_parameter;
+  expect_diagnostic = {};
   ParseFiles({
       {"IFoo.aidl",
        "interface IFoo { @SuppressWarnings(value={\"inout-parameter\"}) void foo(inout Bar b); }"},
@@ -98,7 +104,8 @@
 }
 
 TEST_F(DiagnosticsTest, inout_parameter_SuppressAtDeclLevel) {
-  expect_diagnostics = {};
+  enable_diagnostic = DiagnosticID::inout_parameter;
+  expect_diagnostic = {};
   ParseFiles({
       {"IFoo.aidl",
        "@SuppressWarnings(value={\"inout-parameter\"}) interface IFoo { void foo(inout Bar b); }"},
@@ -107,14 +114,14 @@
 }
 
 TEST_F(DiagnosticsTest, UnknownWarning) {
-  expect_diagnostics = {DiagnosticID::unknown_warning};
+  expect_diagnostic = DiagnosticID::unknown_warning;
   ParseFiles({
       {"IFoo.aidl", "@SuppressWarnings(value={\"blahblah\"}) interface IFoo { void foo(); }"},
   });
 }
 
 TEST_F(DiagnosticsTest, CantSuppressUnknownWarning) {
-  expect_diagnostics = {DiagnosticID::unknown_warning};
+  expect_diagnostic = DiagnosticID::unknown_warning;
   ParseFiles({
       {"IFoo.aidl",
        "@SuppressWarnings(value={\"unknown-warning\"})\n"
@@ -123,14 +130,15 @@
 }
 
 TEST_F(DiagnosticsTest, DontMixOnewayWithTwowayMethods) {
-  expect_diagnostics = {DiagnosticID::mixed_oneway};
+  expect_diagnostic = DiagnosticID::mixed_oneway;
   ParseFiles({
       {"IFoo.aidl", "interface IFoo { void foo(); oneway void bar(); }"},
   });
 }
 
 TEST_F(DiagnosticsTest, DontMixOnewayWithTwowayMethodsSuppressedAtMethod) {
-  expect_diagnostics = {};
+  enable_diagnostic = DiagnosticID::mixed_oneway;
+  expect_diagnostic = {};
   ParseFiles({
       {"IFoo.aidl",
        "interface IFoo {\n"
@@ -142,21 +150,22 @@
 
 TEST_F(DiagnosticsTest, OnewayInterfaceIsOkayWithSyntheticMethods) {
   optional_args = "--version 2";  // will add getInterfaceVersion() synthetic method
-  expect_diagnostics = {};
+  enable_diagnostic = DiagnosticID::mixed_oneway;
+  expect_diagnostic = {};
   ParseFiles({
       {"IFoo.aidl", "oneway interface IFoo { void foo(); }"},
   });
 }
 
 TEST_F(DiagnosticsTest, ArraysAsOutputParametersConsideredHarmful) {
-  expect_diagnostics = {DiagnosticID::out_array};
+  expect_diagnostic = DiagnosticID::out_array;
   ParseFiles({
       {"IFoo.aidl", "interface IFoo { void foo(out String[] ret); }"},
   });
 }
 
 TEST_F(DiagnosticsTest, file_descriptor) {
-  expect_diagnostics = {DiagnosticID::file_descriptor};
+  expect_diagnostic = DiagnosticID::file_descriptor;
   ParseFiles({{"IFoo.aidl",
                "interface IFoo {\n"
                "  void foo(in FileDescriptor fd);\n"
@@ -164,7 +173,7 @@
 }
 
 TEST_F(DiagnosticsTest, out_nullable) {
-  expect_diagnostics = {DiagnosticID::out_nullable};
+  expect_diagnostic = DiagnosticID::out_nullable;
   ParseFiles({{"IFoo.aidl",
                "interface IFoo {\n"
                "  void foo(out @nullable Bar bar);\n"
@@ -173,7 +182,7 @@
 }
 
 TEST_F(DiagnosticsTest, inout_nullable) {
-  expect_diagnostics = {DiagnosticID::out_nullable};
+  expect_diagnostic = DiagnosticID::out_nullable;
   ParseFiles({{"IFoo.aidl",
                "interface IFoo {\n"
                "  void foo(inout @nullable Bar bar);\n"
@@ -182,7 +191,7 @@
 }
 
 TEST_F(DiagnosticsTest, out_nullable_OkayForArrays) {
-  expect_diagnostics = {DiagnosticID::out_array};  // not triggering out_nullable
+  expect_diagnostic = DiagnosticID::out_array;  // not triggering out_nullable
   ParseFiles({{"IFoo.aidl",
                "interface IFoo {\n"
                "  void foo(inout @nullable Bar[] bar1, out @nullable Bar[] bar2);\n"
@@ -191,7 +200,7 @@
 }
 
 TEST_F(DiagnosticsTest, RejectImportsCollisionWithTopLevelDecl) {
-  expect_diagnostics = {DiagnosticID::unique_import};
+  expect_diagnostic = DiagnosticID::unique_import;
   ParseFiles({{"p/IFoo.aidl",
                "package p;\n"
                "import q.IFoo;\n"  // should collide with previous import
@@ -200,7 +209,7 @@
 }
 
 TEST_F(DiagnosticsTest, RejectImportsCollision) {
-  expect_diagnostics = {DiagnosticID::unique_import};
+  expect_diagnostic = DiagnosticID::unique_import;
   ParseFiles({{"p/IFoo.aidl",
                "package p;\n"
                "import q.IBar;\n"
@@ -211,7 +220,8 @@
 }
 
 TEST_F(DiagnosticsTest, AllowImportingSelf) {
-  expect_diagnostics = {};
+  enable_diagnostic = DiagnosticID::unique_import;
+  expect_diagnostic = {};
   ParseFiles({{"p/IFoo.aidl",
                "package p;\n"
                "import p.IFoo;\n"
@@ -219,7 +229,7 @@
 }
 
 TEST_F(DiagnosticsTest, RedundantImports) {
-  expect_diagnostics = {DiagnosticID::unique_import};
+  expect_diagnostic = DiagnosticID::unique_import;
   ParseFiles({{"p/IFoo.aidl",
                "package p;\n"
                "import q.IBar;\n"
@@ -229,22 +239,45 @@
 }
 
 TEST_F(DiagnosticsTest, UntypedCollectionInterface) {
-  expect_diagnostics = {DiagnosticID::untyped_collection};
+  expect_diagnostic = DiagnosticID::untyped_collection;
   ParseFiles({{"IFoo.aidl", "interface IFoo { void foo(in Map m); }"}});
 }
 
 TEST_F(DiagnosticsTest, UntypedCollectionParcelable) {
-  expect_diagnostics = {DiagnosticID::untyped_collection};
+  expect_diagnostic = DiagnosticID::untyped_collection;
   ParseFiles({{"Foo.aidl", "parcelable Foo { Map m; }"}});
 }
 
 TEST_F(DiagnosticsTest, UntypedCollectionUnion) {
-  expect_diagnostics = {DiagnosticID::untyped_collection};
+  expect_diagnostic = DiagnosticID::untyped_collection;
   ParseFiles({{"Foo.aidl", "union Foo { List l; }"}});
 }
 
 TEST_F(DiagnosticsTest, UntypedCollectionInTypeArg) {
-  expect_diagnostics = {DiagnosticID::untyped_collection};
+  expect_diagnostic = DiagnosticID::untyped_collection;
   ParseFiles({{"IFoo.aidl", "interface IFoo { void foo(in Bar<Map> m); }"},
               {"Bar.aidl", "parcelable Bar<T> {}"}});
-}
\ No newline at end of file
+}
+
+TEST_F(DiagnosticsTest, PermissionMissing) {
+  expect_diagnostic = DiagnosticID::missing_permission_annotation;
+  ParseFiles({{"IFoo.aidl", "interface IFoo { void food(); }"}});
+}
+
+TEST_F(DiagnosticsTest, PermissionMethod) {
+  enable_diagnostic = DiagnosticID::missing_permission_annotation;
+  expect_diagnostic = {};
+  ParseFiles({{"IFoo.aidl", "interface IFoo { @EnforcePermission(\"INTERNET\") void food(); }"}});
+}
+
+TEST_F(DiagnosticsTest, PermissionMethodMissing) {
+  expect_diagnostic = DiagnosticID::missing_permission_annotation;
+  ParseFiles({{"IFoo.aidl",
+               "interface IFoo { @EnforcePermission(\"INTERNET\") void food(); void foo2(); }"}});
+}
+
+TEST_F(DiagnosticsTest, PermissionInterface) {
+  enable_diagnostic = DiagnosticID::missing_permission_annotation;
+  expect_diagnostic = {};
+  ParseFiles({{"IFoo.aidl", "@EnforcePermission(\"INTERNET\") interface IFoo { void food(); }"}});
+}
diff --git a/generate_cpp.cpp b/generate_cpp.cpp
index 1ea2010..988ac2b 100644
--- a/generate_cpp.cpp
+++ b/generate_cpp.cpp
@@ -469,6 +469,10 @@
               kTraceVarName, interface.GetName().c_str(), method.GetName().c_str());
   }
 
+  if (interface.EnforceExpression() || method.GetType().EnforceExpression()) {
+    out.Write("#error Permission checks not implemented for the cpp backend\n");
+  }
+
   // Deserialize each "in" parameter to the transaction.
   for (const auto& a: method.GetArguments()) {
     // Deserialization looks roughly like:
diff --git a/generate_java.cpp b/generate_java.cpp
index a8dd232..3420669 100644
--- a/generate_java.cpp
+++ b/generate_java.cpp
@@ -436,7 +436,7 @@
         .parcel = parcel_variable->name,
         .var = field->GetName(),
         .min_sdk_version = options.GetMinSdkVersion(),
-        .is_return_value = false,
+        .write_to_parcel_flag = "_aidl_flag",
     };
     WriteToParcelFor(context);
     writer->Close();
@@ -603,6 +603,9 @@
 }
 
 void GenerateEnumClass(CodeWriter& out, const AidlEnumDeclaration& enum_decl) {
+  const AidlTypeSpecifier& backing_type = enum_decl.GetBackingType();
+  std::string raw_type = JavaSignatureOf(backing_type);
+  std::string boxing_type = JavaBoxingTypeOf(backing_type);
   out << GenerateComments(enum_decl);
   out << GenerateAnnotations(enum_decl);
   out << "public ";
@@ -614,9 +617,43 @@
   for (const auto& enumerator : enum_decl.GetEnumerators()) {
     out << GenerateComments(*enumerator);
     out << GenerateAnnotations(*enumerator);
-    out << fmt::format("public static final {} {} = {};\n",
-                       JavaSignatureOf(enum_decl.GetBackingType()), enumerator->GetName(),
-                       enumerator->ValueString(enum_decl.GetBackingType(), ConstantValueDecorator));
+    out << fmt::format("public static final {} {} = {};\n", raw_type, enumerator->GetName(),
+                       enumerator->ValueString(backing_type, ConstantValueDecorator));
+  }
+  if (enum_decl.JavaDerive("toString")) {
+    out << "interface $ {\n";
+    out.Indent();
+    out << "static String toString(" << raw_type << " _aidl_v) {\n";
+    out.Indent();
+    for (const auto& enumerator : enum_decl.GetEnumerators()) {
+      out << "if (_aidl_v == " << enumerator->GetName() << ") return \"" << enumerator->GetName()
+          << "\";\n";
+    }
+    out << "return " << boxing_type << ".toString(_aidl_v);\n";
+    out.Dedent();
+    out << "}\n";
+    out << fmt::format(R"(static String arrayToString(Object _aidl_v) {{
+  if (_aidl_v == null) return "null";
+  Class<?> _aidl_cls = _aidl_v.getClass();
+  if (!_aidl_cls.isArray()) throw new IllegalArgumentException("not an array: " + _aidl_v);
+  Class<?> comp = _aidl_cls.getComponentType();
+  java.util.StringJoiner _aidl_sj = new java.util.StringJoiner(", ", "[", "]");
+  if (comp.isArray()) {{
+    for (int _aidl_i = 0; _aidl_i < java.lang.reflect.Array.getLength(_aidl_v); _aidl_i++) {{
+      _aidl_sj.add(arrayToString(java.lang.reflect.Array.get(_aidl_v, _aidl_i)));
+    }}
+  }} else {{
+    if (_aidl_cls != {raw_type}[].class) throw new IllegalArgumentException("wrong type: " + _aidl_cls);
+    for ({raw_type} e : ({raw_type}[]) _aidl_v) {{
+      _aidl_sj.add(toString(e));
+    }}
+  }}
+  return _aidl_sj.toString();
+}}
+)",
+                       fmt::arg("raw_type", raw_type));
+    out.Dedent();
+    out << "}\n";
   }
   out.Dedent();
   out << "}\n";
@@ -760,7 +797,7 @@
         .parcel = parcel,
         .var = name,
         .min_sdk_version = options.GetMinSdkVersion(),
-        .is_return_value = false,
+        .write_to_parcel_flag = "_aidl_flag",
     };
     WriteToParcelFor(context);
     writer->Close();
@@ -904,9 +941,6 @@
 
 std::vector<std::string> GenerateJavaAnnotations(const AidlAnnotatable& a) {
   std::vector<std::string> result;
-  if (a.IsHide()) {
-    result.emplace_back("@android.annotation.Hide");
-  }
 
   const AidlAnnotation* unsupported_app_usage = a.UnsupportedAppUsage();
   if (unsupported_app_usage != nullptr) {
@@ -945,6 +979,11 @@
   void Visit(const AidlStructuredParcelable& t) override { ForDefinedType(t); }
   void Visit(const AidlUnionDecl& t) override { ForDefinedType(t); }
   void Visit(const AidlEnumDeclaration& t) override { ForDefinedType(t); }
+  void Visit(const AidlEnumerator& e) override {
+    if (e.IsDeprecated()) {
+      result.push_back("@Deprecated");
+    }
+  }
   void Visit(const AidlMethod& m) override { ForMember(m); }
   void Visit(const AidlConstantDeclaration& c) override { ForMember(c); }
   void Visit(const AidlVariableDeclaration& v) override { ForMember(v); }
diff --git a/generate_java_binder.cpp b/generate_java_binder.cpp
index 0400c52..6f2eacc 100644
--- a/generate_java_binder.cpp
+++ b/generate_java_binder.cpp
@@ -392,7 +392,8 @@
       .parcel = parcel,
       .var = var,
       .min_sdk_version = min_sdk_version,
-      .is_return_value = is_return_value,
+      .write_to_parcel_flag =
+          is_return_value ? "android.os.Parcelable.PARCELABLE_WRITE_RETURN_VALUE" : "0",
   };
   WriteToParcelFor(context);
   writer->Close();
@@ -466,12 +467,13 @@
         break;
       }
     }
-    auto checkPermission = std::make_shared<MethodCall>(
-        THIS_VALUE, "permissionCheckerWrapper",
-        std::vector<std::shared_ptr<Expression>>{
-            std::make_shared<LiteralExpression>("android.Manifest.permission." + permission),
-            std::make_shared<MethodCall>(THIS_VALUE, "getCallingPid"),
-            std::make_shared<LiteralExpression>(attributionSource)});
+    auto permissionName = android::aidl::perm::JavaFullName(permission);
+    auto checkPermission =
+        std::make_shared<MethodCall>(THIS_VALUE, "permissionCheckerWrapper",
+                                     std::vector<std::shared_ptr<Expression>>{
+                                         std::make_shared<LiteralExpression>(permissionName),
+                                         std::make_shared<MethodCall>(THIS_VALUE, "getCallingPid"),
+                                         std::make_shared<LiteralExpression>(attributionSource)});
     return checkPermission;
   }
 
@@ -799,8 +801,14 @@
 
   // TODO(b/151102494): annotation is applied on the return type
   if (method.GetType().IsPropagateAllowBlocking()) {
-    tryStatement->statements->Add(
-        std::make_shared<LiteralStatement>("_reply.setPropagateAllowBlocking();\n"));
+    if (options.GetMinSdkVersion() < JAVA_PROPAGATE_VERSION) {
+      tryStatement->statements->Add(std::make_shared<LiteralStatement>(
+          "if (android.os.Build.VERSION.SDK_INT >= " + std::to_string(JAVA_PROPAGATE_VERSION) +
+          ") { _reply.setPropagateAllowBlocking(); }\n"));
+    } else {
+      tryStatement->statements->Add(
+          std::make_shared<LiteralStatement>("_reply.setPropagateAllowBlocking();\n"));
+    }
   }
 
   // If the transaction returns false, which means UNKNOWN_TRANSACTION, fall back to the local
diff --git a/generate_ndk.cpp b/generate_ndk.cpp
index d7b6663..4345a74 100644
--- a/generate_ndk.cpp
+++ b/generate_ndk.cpp
@@ -288,7 +288,7 @@
         includes.insert("chrono");
         includes.insert("sstream");
       }
-      // For nested interfacees client/server classes are defined in the same header.
+      // For nested interfaces client/server classes are defined in the same header.
       // So we need includes for client/server class as well.
       if (interface.GetParentType()) {
         includes.insert("android/binder_ibinder.h");
@@ -604,6 +604,11 @@
 
   out << "case " << MethodId(method) << ": {\n";
   out.Indent();
+
+  if (defined_type.EnforceExpression() || method.GetType().EnforceExpression()) {
+    out.Write("#error Permission checks not implemented for the ndk backend\n");
+  }
+
   for (const auto& arg : method.GetArguments()) {
     out << NdkNameOf(types, arg->GetType(), StorageMode::STACK) << " " << cpp::BuildVarName(*arg)
         << ";\n";
@@ -980,6 +985,59 @@
   LeaveNdkNamespace(out, defined_type);
 }
 
+void GenerateDelegatorClassDecl(CodeWriter& out, const AidlTypenames& types,
+                                const AidlInterface& defined_type, const Options& options) {
+  const std::string clazz = ClassName(defined_type, ClassNames::DELEGATOR_IMPL);
+  const std::string iface = ClassName(defined_type, ClassNames::INTERFACE);
+  const std::string bn_name = ClassName(defined_type, ClassNames::SERVER);
+  const std::string kDelegateImplVarName = "_impl";
+  const std::string kStatusType = "::ndk::ScopedAStatus";
+
+  out << "class";
+  cpp::GenerateDeprecated(out, defined_type);
+  out << " " << clazz << " : public " << bn_name << " {\n";
+  out << "public:\n";
+  out.Indent();
+  out << "explicit " << clazz << "(const std::shared_ptr<" << iface << "> &impl)"
+      << " : " << kDelegateImplVarName << "(impl) {\n";
+  if (options.Version() > 0) {
+    // TODO(b/222347502) If we need to support mismatched versions of delegator and
+    // impl, this check will be removed. The NDK backend can't override the
+    // getInterface* meta methods because they are marked "final". Removing
+    // "final" changes the ABI and breaks prebuilts.
+    out << "   int32_t _impl_ver = 0;\n";
+    out << "   if (!impl->" << kGetInterfaceVersion << "(&_impl_ver).isOk()) {;\n";
+    out << "      __assert2(__FILE__, __LINE__, __PRETTY_FUNCTION__, \"Delegator failed to get "
+           "version of the implementation.\");\n";
+    out << "   }\n";
+    out << "   if (_impl_ver != " << iface << "::" << kVersion << ") {\n";
+    out << "      __assert2(__FILE__, __LINE__, __PRETTY_FUNCTION__, \"Mismatched versions of "
+           "delegator and implementation is not allowed.\");\n";
+    out << "   }\n";
+  }
+  out << "}\n\n";
+  for (const auto& method : defined_type.GetMethods()) {
+    if (method->IsUserDefined()) {
+      out << kStatusType << " " << method->GetName() << "("
+          << NdkArgList(types, *method, FormatArgForDecl) << ") override";
+      cpp::GenerateDeprecated(out, *method);
+      out << " {\n"
+          << "  return " << kDelegateImplVarName << "->" << method->GetName() << "("
+          << NdkArgList(types, *method, FormatArgNameOnly) << ");\n";
+      out << "}\n";
+    }
+  }
+  out.Dedent();
+  out << "protected:\n";
+  out.Indent();
+  out.Dedent();
+  out << "private:\n";
+  out.Indent();
+  out << "std::shared_ptr<" << iface << "> " << kDelegateImplVarName << ";\n";
+  out.Dedent();
+  out << "};\n\n";
+}
+
 void GenerateServerClassDecl(CodeWriter& out, const AidlTypenames& types,
                              const AidlInterface& defined_type, const Options& options) {
   const std::string clazz = ClassName(defined_type, ClassNames::SERVER);
@@ -1028,9 +1086,18 @@
       << "\"\n";
   out << "\n";
   out << "#include <android/binder_ibinder.h>\n";
+  // Needed for *Delegator classes while delegator version is required to be
+  // the same as the implementation version
+  // TODO(b/222347502) If we ever need to support mismatched versions of delegator and
+  // impl, this include can be removed.
+  out << "#include <cassert>\n\n";
+  // TODO(b/31559095) bionic on host should define __assert2
+  out << "#ifndef __BIONIC__\n#ifndef __assert2\n#define __assert2(a,b,c,d) "
+         "((void)0)\n#endif\n#endif\n";
   out << "\n";
   EnterNdkNamespace(out, defined_type);
   GenerateServerClassDecl(out, types, defined_type, options);
+  GenerateDelegatorClassDecl(out, types, defined_type, options);
   LeaveNdkNamespace(out, defined_type);
 }
 
diff --git a/generate_rust.cpp b/generate_rust.cpp
index 079a6c7..5316c45 100644
--- a/generate_rust.cpp
+++ b/generate_rust.cpp
@@ -387,11 +387,15 @@
   out << "}\n";
 }
 
-void GenerateServerTransaction(CodeWriter& out, const AidlMethod& method,
-                               const AidlTypenames& typenames) {
+void GenerateServerTransaction(CodeWriter& out, const AidlInterface& interface,
+                               const AidlMethod& method, const AidlTypenames& typenames) {
   out << "transactions::" << method.GetName() << " => {\n";
   out.Indent();
 
+  if (interface.EnforceExpression() || method.GetType().EnforceExpression()) {
+    out << "compile_error!(\"Permission checks not support for the Rust backend\");\n";
+  }
+
   string args;
   for (const auto& arg : method.GetArguments()) {
     string arg_name = kArgumentPrefix + arg->GetName();
@@ -498,7 +502,7 @@
   out << "match _aidl_code {\n";
   out.Indent();
   for (const auto& method : iface->GetMethods()) {
-    GenerateServerTransaction(out, *method, typenames);
+    GenerateServerTransaction(out, *iface, *method, typenames);
   }
   out << "_ => Err(binder::StatusCode::UNKNOWN_TRANSACTION)\n";
   out.Dedent();
@@ -1104,6 +1108,7 @@
   code_writer->Indent();
   for (const auto& enumerator : enum_decl->GetEnumerators()) {
     auto value = enumerator->GetValue()->ValueString(aidl_backing_type, ConstantValueDecorator);
+    GenerateDeprecated(*code_writer, *enumerator);
     *code_writer << enumerator->GetName() << " = " << value << ",\n";
   }
   code_writer->Dedent();
diff --git a/options.h b/options.h
index 204b371..5208424 100644
--- a/options.h
+++ b/options.h
@@ -39,7 +39,7 @@
 constexpr uint32_t DEFAULT_SDK_VERSION_RUST = 31;
 
 constexpr uint32_t SDK_VERSION_current = 10000;
-constexpr uint32_t SDK_VERSION_Tiramisu = SDK_VERSION_current;
+constexpr uint32_t SDK_VERSION_Tiramisu = 33;
 
 constexpr uint32_t JAVA_PROPAGATE_VERSION = SDK_VERSION_Tiramisu;
 
diff --git a/options_unittest.cpp b/options_unittest.cpp
index 1121402..2931bc6 100644
--- a/options_unittest.cpp
+++ b/options_unittest.cpp
@@ -454,13 +454,13 @@
   EXPECT_EQ(30u, options->GetMinSdkVersion());
 }
 
-TEST(OPtionsTests, AcceptCodeNameAsMinSdkVersion) {
+TEST(OptionsTests, AcceptCodeNameAsMinSdkVersion) {
   const char* args[] = {
       "aidl", "--lang=java", "--min_sdk_version=Tiramisu", "--out=out", "input.aidl", nullptr,
   };
   auto options = GetOptions(args);
   EXPECT_TRUE(options->Ok());
-  EXPECT_EQ(10000u, options->GetMinSdkVersion());
+  EXPECT_EQ(33u, options->GetMinSdkVersion());
 }
 
 TEST(OptionsTest, DefaultMinSdkVersion) {
diff --git a/parser.cpp b/parser.cpp
index 9988f56..edb110b 100644
--- a/parser.cpp
+++ b/parser.cpp
@@ -28,6 +28,29 @@
 YY_BUFFER_STATE yy_scan_buffer(char*, size_t, void*);
 void yy_delete_buffer(YY_BUFFER_STATE, void*);
 
+// For each union, generate nested "Tag" enum type so that "Tag" can be used as a valid type.
+//    union Foo { int a; int b; } => union Foo { ... enum Tag { a, b }}
+struct UnionTagGenerater : AidlVisitor {
+  void Visit(const AidlUnionDecl& decl) override {
+    std::vector<std::unique_ptr<AidlEnumerator>> enumerators;
+    for (const auto& field : decl.GetFields()) {
+      enumerators.push_back(std::make_unique<AidlEnumerator>(AIDL_LOCATION_HERE, field->GetName(),
+                                                             nullptr, field->GetComments()));
+    }
+    auto tag_enum = std::make_unique<AidlEnumDeclaration>(AIDL_LOCATION_HERE, "Tag", &enumerators,
+                                                          decl.GetPackage(), Comments{});
+    // Tag for @FixedSize union is limited to "byte" type so that it can be passed via FMQ with
+    // with lower overhead.
+    std::shared_ptr<AidlConstantValue> backing_type{
+        AidlConstantValue::String(AIDL_LOCATION_HERE, decl.IsFixedSize() ? "\"byte\"" : "\"int\"")};
+    std::vector<std::unique_ptr<AidlAnnotation>> annotations;
+    annotations.push_back(
+        AidlAnnotation::Parse(AIDL_LOCATION_HERE, "Backing", {{"type", backing_type}}, Comments{}));
+    tag_enum->Annotate(std::move(annotations));
+    const_cast<AidlUnionDecl&>(decl).AddType(std::move(tag_enum));
+  }
+};
+
 const AidlDocument* Parser::Parse(const std::string& filename,
                                   const android::aidl::IoDelegate& io_delegate,
                                   AidlTypenames& typenames, bool is_preprocessed) {
@@ -55,6 +78,10 @@
     return nullptr;
   }
 
+  // Preprocess parsed document before adding to typenames.
+  UnionTagGenerater v;
+  VisitTopDown(v, *parser.document_);
+
   // transfer ownership to AidlTypenames and return the raw pointer
   const AidlDocument* result = parser.document_.get();
   if (!typenames.AddDocument(std::move(parser.document_))) {
diff --git a/permission.cpp b/permission.cpp
index 96cff20..9f730da 100644
--- a/permission.cpp
+++ b/permission.cpp
@@ -28,7 +28,7 @@
 
 std::string AsJavaAnnotation(const Expression& expr) {
   if (const auto& s = std::get_if<std::string>(&expr); s) {
-    return JavaAnnotation(*s);
+    return JavaFullName(*s);
   }
   if (const auto& all = std::get_if<AllOf>(&expr); all) {
     return all->JavaAnnotation();
@@ -39,8 +39,11 @@
   return "";
 }
 
-std::string JavaAnnotation(const std::string& permission) {
-  return "android.Manifest.permission." + permission;
+std::string JavaFullName(const std::string& permission) {
+  if (permission.find('.') == std::string::npos) {
+    return "android.Manifest.permission." + permission;
+  }
+  return permission;
 }
 
 }  // namespace perm
diff --git a/permission.h b/permission.h
index cd87d5c..590a00c 100644
--- a/permission.h
+++ b/permission.h
@@ -32,7 +32,7 @@
 
 typedef std::variant<std::string, AnyOf, AllOf> Expression;
 std::string AsJavaAnnotation(const Expression& expr);
-std::string JavaAnnotation(const std::string& permission);
+std::string JavaFullName(const std::string& permission);
 
 struct AnyOf {
   std::vector<std::string> operands;
@@ -40,7 +40,7 @@
   std::string JavaAnnotation() const {
     std::string ret("anyOf = {");
     for (size_t i = 0; i < operands.size(); i++) {
-      ret += android::aidl::perm::JavaAnnotation(operands[i]);
+      ret += android::aidl::perm::JavaFullName(operands[i]);
       if (i != operands.size() - 1) {
         ret += ", ";
       }
@@ -55,7 +55,7 @@
   std::string JavaAnnotation() const {
     std::string ret("allOf = {");
     for (size_t i = 0; i < operands.size(); i++) {
-      ret += android::aidl::perm::JavaAnnotation(operands[i]);
+      ret += android::aidl::perm::JavaFullName(operands[i]);
       if (i != operands.size() - 1) {
         ret += ", ";
       }
diff --git a/tests/aidl_parser_fuzzer.dict b/tests/aidl_parser_fuzzer.dict
index eb2d8ce..20a270a 100644
--- a/tests/aidl_parser_fuzzer.dict
+++ b/tests/aidl_parser_fuzzer.dict
@@ -49,7 +49,6 @@
 " @SystemApi "
 " @JavaPassthrough "
 " @JavaOnlyStableParcelable "
-" @Hide "
 " @Backing "
 " @FixedSize "
 " @Descriptor "
diff --git a/tests/aidl_test_client_ndk_delegate.cpp b/tests/aidl_test_client_ndk_delegate.cpp
new file mode 100644
index 0000000..6bec01a
--- /dev/null
+++ b/tests/aidl_test_client_ndk_delegate.cpp
@@ -0,0 +1,75 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#include <android/binder_auto_utils.h>
+#include <android/binder_manager.h>
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+
+#include <aidl/android/aidl/tests/BnTestService.h>
+#include <android-base/logging.h>
+
+using aidl::android::aidl::tests::BackendType;
+using aidl::android::aidl::tests::ITestService;
+using aidl::android::aidl::tests::ITestServiceDelegator;
+
+static constexpr int8_t kCustomByte = 8;
+
+struct CustomDelegator : public ITestServiceDelegator {
+ public:
+  CustomDelegator(std::shared_ptr<ITestService>& impl) : ITestServiceDelegator(impl) {}
+
+  // Change RepeatByte to always return the same byte.
+  ndk::ScopedAStatus RepeatByte(int8_t /* token */, int8_t* _aidl_return) override {
+    *_aidl_return = kCustomByte;
+    return ndk::ScopedAStatus::ok();
+  }
+};
+
+struct AidlDelegatorTest : testing::Test {
+  template <typename T>
+  std::shared_ptr<T> getService() {
+    ndk::SpAIBinder binder = ndk::SpAIBinder(AServiceManager_getService(T::descriptor));
+    return T::fromBinder(binder);
+  }
+  void SetUp() override { service = getService<ITestService>(); }
+  std::shared_ptr<ITestService> service;
+};
+
+TEST_F(AidlDelegatorTest, SimpleDelegator) {
+  auto delegator = ndk::SharedRefBase::make<ITestServiceDelegator>(service);
+  int8_t returned_value;
+  auto status = delegator->RepeatByte(12, &returned_value);
+  ASSERT_TRUE(status.isOk()) << status.getMessage();
+  EXPECT_EQ(12, returned_value);
+}
+
+TEST_F(AidlDelegatorTest, CustomDelegator) {
+  auto delegator = ndk::SharedRefBase::make<CustomDelegator>(service);
+  int8_t returned_value;
+  auto status = delegator->RepeatByte(12, &returned_value);
+  ASSERT_TRUE(status.isOk()) << status.getMessage();
+  EXPECT_EQ(kCustomByte, returned_value);
+}
+
+TEST_F(AidlDelegatorTest, SendDelegator) {
+  auto delegator = ndk::SharedRefBase::make<ITestServiceDelegator>(service);
+  auto fromAsBinder = ITestServiceDelegator::fromBinder(delegator->asBinder());
+  // Make sure the delegator works after asBinder -> fromBinder conversions
+  int8_t returned_value = 0;
+  auto status = fromAsBinder->RepeatByte(12, &returned_value);
+  ASSERT_TRUE(status.isOk()) << status.getDescription();
+  EXPECT_EQ(12, returned_value);
+}
diff --git a/tests/aidl_test_client_ndk_parcelables.cpp b/tests/aidl_test_client_ndk_parcelables.cpp
index 72d309d..8b9cd19 100644
--- a/tests/aidl_test_client_ndk_parcelables.cpp
+++ b/tests/aidl_test_client_ndk_parcelables.cpp
@@ -25,6 +25,7 @@
 #include <aidl/android/aidl/fixedsizearray/FixedSizeArrayExample.h>
 #include <aidl/android/aidl/tests/ITestService.h>
 #include <aidl/android/aidl/tests/RecursiveList.h>
+#include <aidl/android/aidl/tests/Union.h>
 
 using aidl::android::aidl::fixedsizearray::FixedSizeArrayExample;
 using BnRepeatFixedSizeArray =
@@ -39,6 +40,7 @@
 using aidl::android::aidl::tests::BackendType;
 using aidl::android::aidl::tests::ITestService;
 using aidl::android::aidl::tests::RecursiveList;
+using aidl::android::aidl::tests::Union;
 using android::OK;
 using ndk::AParcel_readData;
 using ndk::AParcel_writeData;
@@ -79,6 +81,21 @@
   EXPECT_EQ(nullptr, cur);
 }
 
+TEST_F(AidlTest, GetUnionTags) {
+  std::vector<Union> unions;
+  std::vector<Union::Tag> tags;
+  // test empty
+  auto status = getService<ITestService>()->GetUnionTags(unions, &tags);
+  ASSERT_TRUE(status.isOk());
+  EXPECT_EQ(tags, (std::vector<Union::Tag>{}));
+  // test non-empty
+  unions.push_back(Union::make<Union::n>());
+  unions.push_back(Union::make<Union::ns>());
+  status = getService<ITestService>()->GetUnionTags(unions, &tags);
+  ASSERT_TRUE(status.isOk());
+  EXPECT_EQ(tags, (std::vector<Union::Tag>{Union::n, Union::ns}));
+}
+
 TEST_F(AidlTest, FixedSizeArray) {
   auto parcel = AParcel_create();
 
diff --git a/tests/aidl_test_client_ndk_versioned_interface.cpp b/tests/aidl_test_client_ndk_versioned_interface.cpp
index 97da834..f1d0207 100644
--- a/tests/aidl_test_client_ndk_versioned_interface.cpp
+++ b/tests/aidl_test_client_ndk_versioned_interface.cpp
@@ -72,4 +72,4 @@
   EXPECT_EQ(43, ret);
   EXPECT_EQ(0, inoutFoo.intDefault42);
   EXPECT_EQ(0, outFoo.intDefault42);
-}
\ No newline at end of file
+}
diff --git a/tests/aidl_test_client_parcelables.cpp b/tests/aidl_test_client_parcelables.cpp
index 99052bc..119f513 100644
--- a/tests/aidl_test_client_parcelables.cpp
+++ b/tests/aidl_test_client_parcelables.cpp
@@ -211,7 +211,8 @@
 
   // Use std::in_place_index<tag> to avoid "move"
   // Note that make<tag>(...) involves "move" of the content value
-  EXPECT_EQ(Union::make<Union::ns>(3, 0), Union(std::in_place_index<Union::ns>, 3, 0));
+  EXPECT_EQ(Union::make<Union::ns>(3, 0),
+            Union(std::in_place_index<static_cast<size_t>(Union::ns)>, 3, 0));
 
   Union one_two = one_two_three;
   // get<tag> can be used to modify the content
@@ -574,6 +575,21 @@
   EXPECT_EQ(nullptr, cur);
 }
 
+TEST_F(AidlTest, GetUnionTags) {
+  std::vector<Union> unions;
+  std::vector<Union::Tag> tags;
+  // test empty
+  auto status = service->GetUnionTags(unions, &tags);
+  ASSERT_TRUE(status.isOk()) << status.toString8();
+  EXPECT_EQ(tags, (std::vector<Union::Tag>{}));
+  // test non-empty
+  unions.push_back(Union::make<Union::n>());
+  unions.push_back(Union::make<Union::ns>());
+  status = service->GetUnionTags(unions, &tags);
+  ASSERT_TRUE(status.isOk()) << status.toString8();
+  EXPECT_EQ(tags, (std::vector<Union::Tag>{Union::n, Union::ns}));
+}
+
 TEST_F(AidlTest, FixedSizeArray) {
   android::Parcel parcel;
 
diff --git a/tests/aidl_test_service.cpp b/tests/aidl_test_service.cpp
index f000372..a13547f 100644
--- a/tests/aidl_test_service.cpp
+++ b/tests/aidl_test_service.cpp
@@ -742,6 +742,15 @@
     return Status::ok();
   }
 
+  Status GetUnionTags(const std::vector<Union>& input,
+                      std::vector<Union::Tag>* _aidl_return) override {
+    std::vector<Union::Tag> tags;
+    std::transform(input.begin(), input.end(), std::back_inserter(tags),
+                   std::mem_fn(&Union::getTag));
+    *_aidl_return = std::move(tags);
+    return Status::ok();
+  }
+
   Status GetCppJavaTests(sp<IBinder>* ret) {
     *ret = new CppJavaTests;
     return Status::ok();
diff --git a/tests/android/aidl/tests/ITestService.aidl b/tests/android/aidl/tests/ITestService.aidl
index f8e168a..63c0c6c 100644
--- a/tests/android/aidl/tests/ITestService.aidl
+++ b/tests/android/aidl/tests/ITestService.aidl
@@ -25,6 +25,7 @@
 import android.aidl.tests.LongEnum;
 import android.aidl.tests.RecursiveList;
 import android.aidl.tests.StructuredParcelable;
+import android.aidl.tests.Union;
 import android.aidl.tests.extension.ExtendableParcelable;
 
 /**
@@ -259,6 +260,8 @@
     IOldName GetOldNameInterface();
     INewName GetNewNameInterface();
 
+    Union.Tag[] GetUnionTags(in Union[] input);
+
     // Retrieve the ICppJavaTests if the server supports it
     @nullable IBinder GetCppJavaTests();
 
diff --git a/tests/android/aidl/tests/IntEnum.aidl b/tests/android/aidl/tests/IntEnum.aidl
index d9ae6b3..7ab63f3 100644
--- a/tests/android/aidl/tests/IntEnum.aidl
+++ b/tests/android/aidl/tests/IntEnum.aidl
@@ -16,9 +16,12 @@
 
 package android.aidl.tests;
 
+@JavaDerive(toString=true)
 @Backing(type="int")
 enum IntEnum {
     FOO = 1000,
     BAR = 2000,
     BAZ,
+    /** @deprecated do not use this */
+    QUX,
 }
diff --git a/tests/android/aidl/tests/permission/IProtected.aidl b/tests/android/aidl/tests/permission/IProtected.aidl
index 6b46e9d..3407199 100644
--- a/tests/android/aidl/tests/permission/IProtected.aidl
+++ b/tests/android/aidl/tests/permission/IProtected.aidl
@@ -6,4 +6,7 @@
     @EnforcePermission(allOf={"INTERNET", "VIBRATE"}) void MultiplePermissionsAll();
 
     @EnforcePermission(anyOf={"INTERNET", "VIBRATE"}) void MultiplePermissionsAny();
+
+    @EnforcePermission("android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK")
+    void NonManifestPermission();
 }
diff --git a/tests/android/aidl/tests/unions/EnumUnion.aidl b/tests/android/aidl/tests/unions/EnumUnion.aidl
index 5f9bb05..817ea1c 100644
--- a/tests/android/aidl/tests/unions/EnumUnion.aidl
+++ b/tests/android/aidl/tests/unions/EnumUnion.aidl
@@ -23,4 +23,6 @@
 union EnumUnion {
     IntEnum intEnum = IntEnum.FOO;
     LongEnum longEnum;
+    /** @deprecated do not use this */
+    int deprecatedField;
 }
diff --git a/tests/golden_output/aidl-test-fixedsizearray-java-source/gen/android/aidl/fixedsizearray/FixedSizeArrayExample.java b/tests/golden_output/aidl-test-fixedsizearray-java-source/gen/android/aidl/fixedsizearray/FixedSizeArrayExample.java
index f04dd40..a20ae24 100644
--- a/tests/golden_output/aidl-test-fixedsizearray-java-source/gen/android/aidl/fixedsizearray/FixedSizeArrayExample.java
+++ b/tests/golden_output/aidl-test-fixedsizearray-java-source/gen/android/aidl/fixedsizearray/FixedSizeArrayExample.java
@@ -84,61 +84,61 @@
   {
     int _aidl_start_pos = _aidl_parcel.dataPosition();
     _aidl_parcel.writeInt(0);
-    _aidl_parcel.writeFixedArray(int2x3, 0, 2, 3);
-    _aidl_parcel.writeFixedArray(boolArray, 0, 2);
-    _aidl_parcel.writeFixedArray(byteArray, 0, 2);
-    _aidl_parcel.writeFixedArray(charArray, 0, 2);
-    _aidl_parcel.writeFixedArray(intArray, 0, 2);
-    _aidl_parcel.writeFixedArray(longArray, 0, 2);
-    _aidl_parcel.writeFixedArray(floatArray, 0, 2);
-    _aidl_parcel.writeFixedArray(doubleArray, 0, 2);
-    _aidl_parcel.writeFixedArray(stringArray, 0, 2);
-    _aidl_parcel.writeFixedArray(byteEnumArray, 0, 2);
-    _aidl_parcel.writeFixedArray(intEnumArray, 0, 2);
-    _aidl_parcel.writeFixedArray(longEnumArray, 0, 2);
-    _aidl_parcel.writeFixedArray(parcelableArray, 0, 2);
-    _aidl_parcel.writeFixedArray(boolMatrix, 0, 2, 2);
-    _aidl_parcel.writeFixedArray(byteMatrix, 0, 2, 2);
-    _aidl_parcel.writeFixedArray(charMatrix, 0, 2, 2);
-    _aidl_parcel.writeFixedArray(intMatrix, 0, 2, 2);
-    _aidl_parcel.writeFixedArray(longMatrix, 0, 2, 2);
-    _aidl_parcel.writeFixedArray(floatMatrix, 0, 2, 2);
-    _aidl_parcel.writeFixedArray(doubleMatrix, 0, 2, 2);
-    _aidl_parcel.writeFixedArray(stringMatrix, 0, 2, 2);
-    _aidl_parcel.writeFixedArray(byteEnumMatrix, 0, 2, 2);
-    _aidl_parcel.writeFixedArray(intEnumMatrix, 0, 2, 2);
-    _aidl_parcel.writeFixedArray(longEnumMatrix, 0, 2, 2);
-    _aidl_parcel.writeFixedArray(parcelableMatrix, 0, 2, 2);
-    _aidl_parcel.writeFixedArray(boolNullableArray, 0, 2);
-    _aidl_parcel.writeFixedArray(byteNullableArray, 0, 2);
-    _aidl_parcel.writeFixedArray(charNullableArray, 0, 2);
-    _aidl_parcel.writeFixedArray(intNullableArray, 0, 2);
-    _aidl_parcel.writeFixedArray(longNullableArray, 0, 2);
-    _aidl_parcel.writeFixedArray(floatNullableArray, 0, 2);
-    _aidl_parcel.writeFixedArray(doubleNullableArray, 0, 2);
-    _aidl_parcel.writeFixedArray(stringNullableArray, 0, 2);
-    _aidl_parcel.writeFixedArray(byteEnumNullableArray, 0, 2);
-    _aidl_parcel.writeFixedArray(intEnumNullableArray, 0, 2);
-    _aidl_parcel.writeFixedArray(longEnumNullableArray, 0, 2);
-    _aidl_parcel.writeFixedArray(binderNullableArray, 0, 2);
-    _aidl_parcel.writeFixedArray(pfdNullableArray, 0, 2);
-    _aidl_parcel.writeFixedArray(parcelableNullableArray, 0, 2);
-    _aidl_parcel.writeFixedArray(interfaceNullableArray, 0, 2);
-    _aidl_parcel.writeFixedArray(boolNullableMatrix, 0, 2, 2);
-    _aidl_parcel.writeFixedArray(byteNullableMatrix, 0, 2, 2);
-    _aidl_parcel.writeFixedArray(charNullableMatrix, 0, 2, 2);
-    _aidl_parcel.writeFixedArray(intNullableMatrix, 0, 2, 2);
-    _aidl_parcel.writeFixedArray(longNullableMatrix, 0, 2, 2);
-    _aidl_parcel.writeFixedArray(floatNullableMatrix, 0, 2, 2);
-    _aidl_parcel.writeFixedArray(doubleNullableMatrix, 0, 2, 2);
-    _aidl_parcel.writeFixedArray(stringNullableMatrix, 0, 2, 2);
-    _aidl_parcel.writeFixedArray(byteEnumNullableMatrix, 0, 2, 2);
-    _aidl_parcel.writeFixedArray(intEnumNullableMatrix, 0, 2, 2);
-    _aidl_parcel.writeFixedArray(longEnumNullableMatrix, 0, 2, 2);
-    _aidl_parcel.writeFixedArray(binderNullableMatrix, 0, 2, 2);
-    _aidl_parcel.writeFixedArray(pfdNullableMatrix, 0, 2, 2);
-    _aidl_parcel.writeFixedArray(parcelableNullableMatrix, 0, 2, 2);
-    _aidl_parcel.writeFixedArray(interfaceNullableMatrix, 0, 2, 2);
+    _aidl_parcel.writeFixedArray(int2x3, _aidl_flag, 2, 3);
+    _aidl_parcel.writeFixedArray(boolArray, _aidl_flag, 2);
+    _aidl_parcel.writeFixedArray(byteArray, _aidl_flag, 2);
+    _aidl_parcel.writeFixedArray(charArray, _aidl_flag, 2);
+    _aidl_parcel.writeFixedArray(intArray, _aidl_flag, 2);
+    _aidl_parcel.writeFixedArray(longArray, _aidl_flag, 2);
+    _aidl_parcel.writeFixedArray(floatArray, _aidl_flag, 2);
+    _aidl_parcel.writeFixedArray(doubleArray, _aidl_flag, 2);
+    _aidl_parcel.writeFixedArray(stringArray, _aidl_flag, 2);
+    _aidl_parcel.writeFixedArray(byteEnumArray, _aidl_flag, 2);
+    _aidl_parcel.writeFixedArray(intEnumArray, _aidl_flag, 2);
+    _aidl_parcel.writeFixedArray(longEnumArray, _aidl_flag, 2);
+    _aidl_parcel.writeFixedArray(parcelableArray, _aidl_flag, 2);
+    _aidl_parcel.writeFixedArray(boolMatrix, _aidl_flag, 2, 2);
+    _aidl_parcel.writeFixedArray(byteMatrix, _aidl_flag, 2, 2);
+    _aidl_parcel.writeFixedArray(charMatrix, _aidl_flag, 2, 2);
+    _aidl_parcel.writeFixedArray(intMatrix, _aidl_flag, 2, 2);
+    _aidl_parcel.writeFixedArray(longMatrix, _aidl_flag, 2, 2);
+    _aidl_parcel.writeFixedArray(floatMatrix, _aidl_flag, 2, 2);
+    _aidl_parcel.writeFixedArray(doubleMatrix, _aidl_flag, 2, 2);
+    _aidl_parcel.writeFixedArray(stringMatrix, _aidl_flag, 2, 2);
+    _aidl_parcel.writeFixedArray(byteEnumMatrix, _aidl_flag, 2, 2);
+    _aidl_parcel.writeFixedArray(intEnumMatrix, _aidl_flag, 2, 2);
+    _aidl_parcel.writeFixedArray(longEnumMatrix, _aidl_flag, 2, 2);
+    _aidl_parcel.writeFixedArray(parcelableMatrix, _aidl_flag, 2, 2);
+    _aidl_parcel.writeFixedArray(boolNullableArray, _aidl_flag, 2);
+    _aidl_parcel.writeFixedArray(byteNullableArray, _aidl_flag, 2);
+    _aidl_parcel.writeFixedArray(charNullableArray, _aidl_flag, 2);
+    _aidl_parcel.writeFixedArray(intNullableArray, _aidl_flag, 2);
+    _aidl_parcel.writeFixedArray(longNullableArray, _aidl_flag, 2);
+    _aidl_parcel.writeFixedArray(floatNullableArray, _aidl_flag, 2);
+    _aidl_parcel.writeFixedArray(doubleNullableArray, _aidl_flag, 2);
+    _aidl_parcel.writeFixedArray(stringNullableArray, _aidl_flag, 2);
+    _aidl_parcel.writeFixedArray(byteEnumNullableArray, _aidl_flag, 2);
+    _aidl_parcel.writeFixedArray(intEnumNullableArray, _aidl_flag, 2);
+    _aidl_parcel.writeFixedArray(longEnumNullableArray, _aidl_flag, 2);
+    _aidl_parcel.writeFixedArray(binderNullableArray, _aidl_flag, 2);
+    _aidl_parcel.writeFixedArray(pfdNullableArray, _aidl_flag, 2);
+    _aidl_parcel.writeFixedArray(parcelableNullableArray, _aidl_flag, 2);
+    _aidl_parcel.writeFixedArray(interfaceNullableArray, _aidl_flag, 2);
+    _aidl_parcel.writeFixedArray(boolNullableMatrix, _aidl_flag, 2, 2);
+    _aidl_parcel.writeFixedArray(byteNullableMatrix, _aidl_flag, 2, 2);
+    _aidl_parcel.writeFixedArray(charNullableMatrix, _aidl_flag, 2, 2);
+    _aidl_parcel.writeFixedArray(intNullableMatrix, _aidl_flag, 2, 2);
+    _aidl_parcel.writeFixedArray(longNullableMatrix, _aidl_flag, 2, 2);
+    _aidl_parcel.writeFixedArray(floatNullableMatrix, _aidl_flag, 2, 2);
+    _aidl_parcel.writeFixedArray(doubleNullableMatrix, _aidl_flag, 2, 2);
+    _aidl_parcel.writeFixedArray(stringNullableMatrix, _aidl_flag, 2, 2);
+    _aidl_parcel.writeFixedArray(byteEnumNullableMatrix, _aidl_flag, 2, 2);
+    _aidl_parcel.writeFixedArray(intEnumNullableMatrix, _aidl_flag, 2, 2);
+    _aidl_parcel.writeFixedArray(longEnumNullableMatrix, _aidl_flag, 2, 2);
+    _aidl_parcel.writeFixedArray(binderNullableMatrix, _aidl_flag, 2, 2);
+    _aidl_parcel.writeFixedArray(pfdNullableMatrix, _aidl_flag, 2, 2);
+    _aidl_parcel.writeFixedArray(parcelableNullableMatrix, _aidl_flag, 2, 2);
+    _aidl_parcel.writeFixedArray(interfaceNullableMatrix, _aidl_flag, 2, 2);
     int _aidl_end_pos = _aidl_parcel.dataPosition();
     _aidl_parcel.setDataPosition(_aidl_start_pos);
     _aidl_parcel.writeInt(_aidl_end_pos - _aidl_start_pos);
diff --git a/tests/golden_output/aidl-test-interface-cpp-source/gen/android/aidl/tests/ArrayOfInterfaces.cpp b/tests/golden_output/aidl-test-interface-cpp-source/gen/android/aidl/tests/ArrayOfInterfaces.cpp
index 1973f8f..c3df2a0 100644
--- a/tests/golden_output/aidl-test-interface-cpp-source/gen/android/aidl/tests/ArrayOfInterfaces.cpp
+++ b/tests/golden_output/aidl-test-interface-cpp-source/gen/android/aidl/tests/ArrayOfInterfaces.cpp
@@ -393,7 +393,7 @@
   ::android::status_t _aidl_ret_status;
   int32_t _aidl_tag;
   if ((_aidl_ret_status = _aidl_parcel->readInt32(&_aidl_tag)) != ::android::OK) return _aidl_ret_status;
-  switch (_aidl_tag) {
+  switch (static_cast<Tag>(_aidl_tag)) {
   case iface: {
     ::android::sp<::android::aidl::tests::ArrayOfInterfaces::IEmptyInterface> _aidl_value;
     if ((_aidl_ret_status = _aidl_parcel->readStrongBinder(&_aidl_value)) != ::android::OK) return _aidl_ret_status;
@@ -438,7 +438,7 @@
   return ::android::BAD_VALUE;
 }
 ::android::status_t ArrayOfInterfaces::MyUnion::writeToParcel(::android::Parcel* _aidl_parcel) const {
-  ::android::status_t _aidl_ret_status = _aidl_parcel->writeInt32(getTag());
+  ::android::status_t _aidl_ret_status = _aidl_parcel->writeInt32(static_cast<int32_t>(getTag()));
   if (_aidl_ret_status != ::android::OK) return _aidl_ret_status;
   switch (getTag()) {
   case iface: return _aidl_parcel->writeStrongBinder(get<iface>());
diff --git a/tests/golden_output/aidl-test-interface-cpp-source/gen/android/aidl/tests/FixedSize.cpp b/tests/golden_output/aidl-test-interface-cpp-source/gen/android/aidl/tests/FixedSize.cpp
index 73fca2c..320fe27 100644
--- a/tests/golden_output/aidl-test-interface-cpp-source/gen/android/aidl/tests/FixedSize.cpp
+++ b/tests/golden_output/aidl-test-interface-cpp-source/gen/android/aidl/tests/FixedSize.cpp
@@ -179,7 +179,7 @@
   ::android::status_t _aidl_ret_status;
   int32_t _aidl_tag;
   if ((_aidl_ret_status = _aidl_parcel->readInt32(&_aidl_tag)) != ::android::OK) return _aidl_ret_status;
-  switch (_aidl_tag) {
+  switch (static_cast<Tag>(_aidl_tag)) {
   case booleanValue: {
     bool _aidl_value;
     if ((_aidl_ret_status = _aidl_parcel->readBool(&_aidl_value)) != ::android::OK) return _aidl_ret_status;
@@ -264,7 +264,7 @@
   return ::android::BAD_VALUE;
 }
 ::android::status_t FixedSize::FixedUnion::writeToParcel(::android::Parcel* _aidl_parcel) const {
-  ::android::status_t _aidl_ret_status = _aidl_parcel->writeInt32(getTag());
+  ::android::status_t _aidl_ret_status = _aidl_parcel->writeInt32(static_cast<int32_t>(getTag()));
   if (_aidl_ret_status != ::android::OK) return _aidl_ret_status;
   switch (getTag()) {
   case booleanValue: return _aidl_parcel->writeBool(get<booleanValue>());
diff --git a/tests/golden_output/aidl-test-interface-cpp-source/gen/android/aidl/tests/ITestService.cpp b/tests/golden_output/aidl-test-interface-cpp-source/gen/android/aidl/tests/ITestService.cpp
index 40264c0..b7e11ae 100644
--- a/tests/golden_output/aidl-test-interface-cpp-source/gen/android/aidl/tests/ITestService.cpp
+++ b/tests/golden_output/aidl-test-interface-cpp-source/gen/android/aidl/tests/ITestService.cpp
@@ -2527,6 +2527,44 @@
   return _aidl_status;
 }
 
+::android::binder::Status BpTestService::GetUnionTags(const ::std::vector<::android::aidl::tests::Union>& input, ::std::vector<::android::aidl::tests::Union::Tag>* _aidl_return) {
+  ::android::Parcel _aidl_data;
+  _aidl_data.markSensitive();
+  _aidl_data.markForBinder(remoteStrong());
+  ::android::Parcel _aidl_reply;
+  ::android::status_t _aidl_ret_status = ::android::OK;
+  ::android::binder::Status _aidl_status;
+  _aidl_ret_status = _aidl_data.writeInterfaceToken(getInterfaceDescriptor());
+  if (((_aidl_ret_status) != (::android::OK))) {
+    goto _aidl_error;
+  }
+  _aidl_ret_status = _aidl_data.writeParcelableVector(input);
+  if (((_aidl_ret_status) != (::android::OK))) {
+    goto _aidl_error;
+  }
+  _aidl_ret_status = remote()->transact(BnTestService::TRANSACTION_GetUnionTags, _aidl_data, &_aidl_reply, ::android::IBinder::FLAG_CLEAR_BUF);
+  if (UNLIKELY(_aidl_ret_status == ::android::UNKNOWN_TRANSACTION && ITestService::getDefaultImpl())) {
+     return ITestService::getDefaultImpl()->GetUnionTags(input, _aidl_return);
+  }
+  if (((_aidl_ret_status) != (::android::OK))) {
+    goto _aidl_error;
+  }
+  _aidl_ret_status = _aidl_status.readFromParcel(_aidl_reply);
+  if (((_aidl_ret_status) != (::android::OK))) {
+    goto _aidl_error;
+  }
+  if (!_aidl_status.isOk()) {
+    return _aidl_status;
+  }
+  _aidl_ret_status = _aidl_reply.readEnumVector(_aidl_return);
+  if (((_aidl_ret_status) != (::android::OK))) {
+    goto _aidl_error;
+  }
+  _aidl_error:
+  _aidl_status.setFromStatusT(_aidl_ret_status);
+  return _aidl_status;
+}
+
 ::android::binder::Status BpTestService::GetCppJavaTests(::android::sp<::android::IBinder>* _aidl_return) {
   ::android::Parcel _aidl_data;
   _aidl_data.markSensitive();
@@ -4602,6 +4640,36 @@
     }
   }
   break;
+  case BnTestService::TRANSACTION_GetUnionTags:
+  {
+    ::std::vector<::android::aidl::tests::Union> in_input;
+    ::std::vector<::android::aidl::tests::Union::Tag> _aidl_return;
+    if (!(_aidl_data.checkInterface(this))) {
+      _aidl_ret_status = ::android::BAD_TYPE;
+      break;
+    }
+    _aidl_ret_status = _aidl_data.readParcelableVector(&in_input);
+    if (((_aidl_ret_status) != (::android::OK))) {
+      break;
+    }
+    if (auto st = _aidl_data.enforceNoDataAvail(); !st.isOk()) {
+      _aidl_ret_status = st.writeToParcel(_aidl_reply);
+      break;
+    }
+    ::android::binder::Status _aidl_status(GetUnionTags(in_input, &_aidl_return));
+    _aidl_ret_status = _aidl_status.writeToParcel(_aidl_reply);
+    if (((_aidl_ret_status) != (::android::OK))) {
+      break;
+    }
+    if (!_aidl_status.isOk()) {
+      break;
+    }
+    _aidl_ret_status = _aidl_reply->writeEnumVector(_aidl_return);
+    if (((_aidl_ret_status) != (::android::OK))) {
+      break;
+    }
+  }
+  break;
   case BnTestService::TRANSACTION_GetCppJavaTests:
   {
     ::android::sp<::android::IBinder> _aidl_return;
diff --git a/tests/golden_output/aidl-test-interface-cpp-source/gen/android/aidl/tests/ITestService.cpp.d b/tests/golden_output/aidl-test-interface-cpp-source/gen/android/aidl/tests/ITestService.cpp.d
index f82081e..de66202 100644
--- a/tests/golden_output/aidl-test-interface-cpp-source/gen/android/aidl/tests/ITestService.cpp.d
+++ b/tests/golden_output/aidl-test-interface-cpp-source/gen/android/aidl/tests/ITestService.cpp.d
@@ -9,6 +9,6 @@
   system/tools/aidl/tests/android/aidl/tests/LongEnum.aidl \
   system/tools/aidl/tests/android/aidl/tests/RecursiveList.aidl \
   system/tools/aidl/tests/android/aidl/tests/StructuredParcelable.aidl \
+  system/tools/aidl/tests/android/aidl/tests/Union.aidl \
   system/tools/aidl/tests/android/aidl/tests/extension/ExtendableParcelable.aidl \
-  system/tools/aidl/tests/android/aidl/tests/ConstantExpressionEnum.aidl \
-  system/tools/aidl/tests/android/aidl/tests/Union.aidl
+  system/tools/aidl/tests/android/aidl/tests/ConstantExpressionEnum.aidl
diff --git a/tests/golden_output/aidl-test-interface-cpp-source/gen/android/aidl/tests/ListOfInterfaces.cpp b/tests/golden_output/aidl-test-interface-cpp-source/gen/android/aidl/tests/ListOfInterfaces.cpp
index 7a0e43e..dfca641 100644
--- a/tests/golden_output/aidl-test-interface-cpp-source/gen/android/aidl/tests/ListOfInterfaces.cpp
+++ b/tests/golden_output/aidl-test-interface-cpp-source/gen/android/aidl/tests/ListOfInterfaces.cpp
@@ -377,7 +377,7 @@
   ::android::status_t _aidl_ret_status;
   int32_t _aidl_tag;
   if ((_aidl_ret_status = _aidl_parcel->readInt32(&_aidl_tag)) != ::android::OK) return _aidl_ret_status;
-  switch (_aidl_tag) {
+  switch (static_cast<Tag>(_aidl_tag)) {
   case iface: {
     ::android::sp<::android::aidl::tests::ListOfInterfaces::IEmptyInterface> _aidl_value;
     if ((_aidl_ret_status = _aidl_parcel->readStrongBinder(&_aidl_value)) != ::android::OK) return _aidl_ret_status;
@@ -422,7 +422,7 @@
   return ::android::BAD_VALUE;
 }
 ::android::status_t ListOfInterfaces::MyUnion::writeToParcel(::android::Parcel* _aidl_parcel) const {
-  ::android::status_t _aidl_ret_status = _aidl_parcel->writeInt32(getTag());
+  ::android::status_t _aidl_ret_status = _aidl_parcel->writeInt32(static_cast<int32_t>(getTag()));
   if (_aidl_ret_status != ::android::OK) return _aidl_ret_status;
   switch (getTag()) {
   case iface: return _aidl_parcel->writeStrongBinder(get<iface>());
diff --git a/tests/golden_output/aidl-test-interface-cpp-source/gen/android/aidl/tests/Union.cpp b/tests/golden_output/aidl-test-interface-cpp-source/gen/android/aidl/tests/Union.cpp
index 36b8670..bd4eef1 100644
--- a/tests/golden_output/aidl-test-interface-cpp-source/gen/android/aidl/tests/Union.cpp
+++ b/tests/golden_output/aidl-test-interface-cpp-source/gen/android/aidl/tests/Union.cpp
@@ -11,7 +11,7 @@
   ::android::status_t _aidl_ret_status;
   int32_t _aidl_tag;
   if ((_aidl_ret_status = _aidl_parcel->readInt32(&_aidl_tag)) != ::android::OK) return _aidl_ret_status;
-  switch (_aidl_tag) {
+  switch (static_cast<Tag>(_aidl_tag)) {
   case ns: {
     ::std::vector<int32_t> _aidl_value;
     if ((_aidl_ret_status = _aidl_parcel->readInt32Vector(&_aidl_value)) != ::android::OK) return _aidl_ret_status;
@@ -86,7 +86,7 @@
   return ::android::BAD_VALUE;
 }
 ::android::status_t Union::writeToParcel(::android::Parcel* _aidl_parcel) const {
-  ::android::status_t _aidl_ret_status = _aidl_parcel->writeInt32(getTag());
+  ::android::status_t _aidl_ret_status = _aidl_parcel->writeInt32(static_cast<int32_t>(getTag()));
   if (_aidl_ret_status != ::android::OK) return _aidl_ret_status;
   switch (getTag()) {
   case ns: return _aidl_parcel->writeInt32Vector(get<ns>());
diff --git a/tests/golden_output/aidl-test-interface-cpp-source/gen/android/aidl/tests/UnionWithFd.cpp b/tests/golden_output/aidl-test-interface-cpp-source/gen/android/aidl/tests/UnionWithFd.cpp
index 1006527..d8f4103 100644
--- a/tests/golden_output/aidl-test-interface-cpp-source/gen/android/aidl/tests/UnionWithFd.cpp
+++ b/tests/golden_output/aidl-test-interface-cpp-source/gen/android/aidl/tests/UnionWithFd.cpp
@@ -7,7 +7,7 @@
   ::android::status_t _aidl_ret_status;
   int32_t _aidl_tag;
   if ((_aidl_ret_status = _aidl_parcel->readInt32(&_aidl_tag)) != ::android::OK) return _aidl_ret_status;
-  switch (_aidl_tag) {
+  switch (static_cast<Tag>(_aidl_tag)) {
   case num: {
     int32_t _aidl_value;
     if ((_aidl_ret_status = _aidl_parcel->readInt32(&_aidl_value)) != ::android::OK) return _aidl_ret_status;
@@ -32,7 +32,7 @@
   return ::android::BAD_VALUE;
 }
 ::android::status_t UnionWithFd::writeToParcel(::android::Parcel* _aidl_parcel) const {
-  ::android::status_t _aidl_ret_status = _aidl_parcel->writeInt32(getTag());
+  ::android::status_t _aidl_ret_status = _aidl_parcel->writeInt32(static_cast<int32_t>(getTag()));
   if (_aidl_ret_status != ::android::OK) return _aidl_ret_status;
   switch (getTag()) {
   case num: return _aidl_parcel->writeInt32(get<num>());
diff --git a/tests/golden_output/aidl-test-interface-cpp-source/gen/android/aidl/tests/permission/IProtected.cpp b/tests/golden_output/aidl-test-interface-cpp-source/gen/android/aidl/tests/permission/IProtected.cpp
deleted file mode 100644
index 44ad7e3..0000000
--- a/tests/golden_output/aidl-test-interface-cpp-source/gen/android/aidl/tests/permission/IProtected.cpp
+++ /dev/null
@@ -1,197 +0,0 @@
-#include <android/aidl/tests/permission/IProtected.h>
-#include <android/aidl/tests/permission/BpProtected.h>
-namespace android {
-namespace aidl {
-namespace tests {
-namespace permission {
-DO_NOT_DIRECTLY_USE_ME_IMPLEMENT_META_INTERFACE(Protected, "android.aidl.tests.permission.IProtected")
-}  // namespace permission
-}  // namespace tests
-}  // namespace aidl
-}  // namespace android
-#include <android/aidl/tests/permission/BpProtected.h>
-#include <android/aidl/tests/permission/BnProtected.h>
-#include <binder/Parcel.h>
-#include <android-base/macros.h>
-
-namespace android {
-namespace aidl {
-namespace tests {
-namespace permission {
-
-BpProtected::BpProtected(const ::android::sp<::android::IBinder>& _aidl_impl)
-    : BpInterface<IProtected>(_aidl_impl){
-}
-
-::android::binder::Status BpProtected::PermissionProtected() {
-  ::android::Parcel _aidl_data;
-  _aidl_data.markForBinder(remoteStrong());
-  ::android::Parcel _aidl_reply;
-  ::android::status_t _aidl_ret_status = ::android::OK;
-  ::android::binder::Status _aidl_status;
-  _aidl_ret_status = _aidl_data.writeInterfaceToken(getInterfaceDescriptor());
-  if (((_aidl_ret_status) != (::android::OK))) {
-    goto _aidl_error;
-  }
-  _aidl_ret_status = remote()->transact(BnProtected::TRANSACTION_PermissionProtected, _aidl_data, &_aidl_reply, 0);
-  if (UNLIKELY(_aidl_ret_status == ::android::UNKNOWN_TRANSACTION && IProtected::getDefaultImpl())) {
-     return IProtected::getDefaultImpl()->PermissionProtected();
-  }
-  if (((_aidl_ret_status) != (::android::OK))) {
-    goto _aidl_error;
-  }
-  _aidl_ret_status = _aidl_status.readFromParcel(_aidl_reply);
-  if (((_aidl_ret_status) != (::android::OK))) {
-    goto _aidl_error;
-  }
-  if (!_aidl_status.isOk()) {
-    return _aidl_status;
-  }
-  _aidl_error:
-  _aidl_status.setFromStatusT(_aidl_ret_status);
-  return _aidl_status;
-}
-
-::android::binder::Status BpProtected::MultiplePermissionsAll() {
-  ::android::Parcel _aidl_data;
-  _aidl_data.markForBinder(remoteStrong());
-  ::android::Parcel _aidl_reply;
-  ::android::status_t _aidl_ret_status = ::android::OK;
-  ::android::binder::Status _aidl_status;
-  _aidl_ret_status = _aidl_data.writeInterfaceToken(getInterfaceDescriptor());
-  if (((_aidl_ret_status) != (::android::OK))) {
-    goto _aidl_error;
-  }
-  _aidl_ret_status = remote()->transact(BnProtected::TRANSACTION_MultiplePermissionsAll, _aidl_data, &_aidl_reply, 0);
-  if (UNLIKELY(_aidl_ret_status == ::android::UNKNOWN_TRANSACTION && IProtected::getDefaultImpl())) {
-     return IProtected::getDefaultImpl()->MultiplePermissionsAll();
-  }
-  if (((_aidl_ret_status) != (::android::OK))) {
-    goto _aidl_error;
-  }
-  _aidl_ret_status = _aidl_status.readFromParcel(_aidl_reply);
-  if (((_aidl_ret_status) != (::android::OK))) {
-    goto _aidl_error;
-  }
-  if (!_aidl_status.isOk()) {
-    return _aidl_status;
-  }
-  _aidl_error:
-  _aidl_status.setFromStatusT(_aidl_ret_status);
-  return _aidl_status;
-}
-
-::android::binder::Status BpProtected::MultiplePermissionsAny() {
-  ::android::Parcel _aidl_data;
-  _aidl_data.markForBinder(remoteStrong());
-  ::android::Parcel _aidl_reply;
-  ::android::status_t _aidl_ret_status = ::android::OK;
-  ::android::binder::Status _aidl_status;
-  _aidl_ret_status = _aidl_data.writeInterfaceToken(getInterfaceDescriptor());
-  if (((_aidl_ret_status) != (::android::OK))) {
-    goto _aidl_error;
-  }
-  _aidl_ret_status = remote()->transact(BnProtected::TRANSACTION_MultiplePermissionsAny, _aidl_data, &_aidl_reply, 0);
-  if (UNLIKELY(_aidl_ret_status == ::android::UNKNOWN_TRANSACTION && IProtected::getDefaultImpl())) {
-     return IProtected::getDefaultImpl()->MultiplePermissionsAny();
-  }
-  if (((_aidl_ret_status) != (::android::OK))) {
-    goto _aidl_error;
-  }
-  _aidl_ret_status = _aidl_status.readFromParcel(_aidl_reply);
-  if (((_aidl_ret_status) != (::android::OK))) {
-    goto _aidl_error;
-  }
-  if (!_aidl_status.isOk()) {
-    return _aidl_status;
-  }
-  _aidl_error:
-  _aidl_status.setFromStatusT(_aidl_ret_status);
-  return _aidl_status;
-}
-
-}  // namespace permission
-}  // namespace tests
-}  // namespace aidl
-}  // namespace android
-#include <android/aidl/tests/permission/BnProtected.h>
-#include <binder/Parcel.h>
-#include <binder/Stability.h>
-
-namespace android {
-namespace aidl {
-namespace tests {
-namespace permission {
-
-BnProtected::BnProtected()
-{
-  ::android::internal::Stability::markCompilationUnit(this);
-}
-
-::android::status_t BnProtected::onTransact(uint32_t _aidl_code, const ::android::Parcel& _aidl_data, ::android::Parcel* _aidl_reply, uint32_t _aidl_flags) {
-  ::android::status_t _aidl_ret_status = ::android::OK;
-  switch (_aidl_code) {
-  case BnProtected::TRANSACTION_PermissionProtected:
-  {
-    if (!(_aidl_data.checkInterface(this))) {
-      _aidl_ret_status = ::android::BAD_TYPE;
-      break;
-    }
-    ::android::binder::Status _aidl_status(PermissionProtected());
-    _aidl_ret_status = _aidl_status.writeToParcel(_aidl_reply);
-    if (((_aidl_ret_status) != (::android::OK))) {
-      break;
-    }
-    if (!_aidl_status.isOk()) {
-      break;
-    }
-  }
-  break;
-  case BnProtected::TRANSACTION_MultiplePermissionsAll:
-  {
-    if (!(_aidl_data.checkInterface(this))) {
-      _aidl_ret_status = ::android::BAD_TYPE;
-      break;
-    }
-    ::android::binder::Status _aidl_status(MultiplePermissionsAll());
-    _aidl_ret_status = _aidl_status.writeToParcel(_aidl_reply);
-    if (((_aidl_ret_status) != (::android::OK))) {
-      break;
-    }
-    if (!_aidl_status.isOk()) {
-      break;
-    }
-  }
-  break;
-  case BnProtected::TRANSACTION_MultiplePermissionsAny:
-  {
-    if (!(_aidl_data.checkInterface(this))) {
-      _aidl_ret_status = ::android::BAD_TYPE;
-      break;
-    }
-    ::android::binder::Status _aidl_status(MultiplePermissionsAny());
-    _aidl_ret_status = _aidl_status.writeToParcel(_aidl_reply);
-    if (((_aidl_ret_status) != (::android::OK))) {
-      break;
-    }
-    if (!_aidl_status.isOk()) {
-      break;
-    }
-  }
-  break;
-  default:
-  {
-    _aidl_ret_status = ::android::BBinder::onTransact(_aidl_code, _aidl_data, _aidl_reply, _aidl_flags);
-  }
-  break;
-  }
-  if (_aidl_ret_status == ::android::UNEXPECTED_NULL) {
-    _aidl_ret_status = ::android::binder::Status::fromExceptionCode(::android::binder::Status::EX_NULL_POINTER).writeOverParcel(_aidl_reply);
-  }
-  return _aidl_ret_status;
-}
-
-}  // namespace permission
-}  // namespace tests
-}  // namespace aidl
-}  // namespace android
diff --git a/tests/golden_output/aidl-test-interface-cpp-source/gen/android/aidl/tests/permission/IProtected.cpp.d b/tests/golden_output/aidl-test-interface-cpp-source/gen/android/aidl/tests/permission/IProtected.cpp.d
deleted file mode 100644
index 3fb3aee..0000000
--- a/tests/golden_output/aidl-test-interface-cpp-source/gen/android/aidl/tests/permission/IProtected.cpp.d
+++ /dev/null
@@ -1,2 +0,0 @@
-out/soong/.intermediates/system/tools/aidl/aidl-test-interface-cpp-source/gen/android/aidl/tests/permission/IProtected.cpp : \
-  system/tools/aidl/tests/android/aidl/tests/permission/IProtected.aidl
diff --git a/tests/golden_output/aidl-test-interface-cpp-source/gen/android/aidl/tests/permission/IProtectedInterface.cpp b/tests/golden_output/aidl-test-interface-cpp-source/gen/android/aidl/tests/permission/IProtectedInterface.cpp
deleted file mode 100644
index bd9fe9d..0000000
--- a/tests/golden_output/aidl-test-interface-cpp-source/gen/android/aidl/tests/permission/IProtectedInterface.cpp
+++ /dev/null
@@ -1,152 +0,0 @@
-#include <android/aidl/tests/permission/IProtectedInterface.h>
-#include <android/aidl/tests/permission/BpProtectedInterface.h>
-namespace android {
-namespace aidl {
-namespace tests {
-namespace permission {
-DO_NOT_DIRECTLY_USE_ME_IMPLEMENT_META_INTERFACE(ProtectedInterface, "android.aidl.tests.permission.IProtectedInterface")
-}  // namespace permission
-}  // namespace tests
-}  // namespace aidl
-}  // namespace android
-#include <android/aidl/tests/permission/BpProtectedInterface.h>
-#include <android/aidl/tests/permission/BnProtectedInterface.h>
-#include <binder/Parcel.h>
-#include <android-base/macros.h>
-
-namespace android {
-namespace aidl {
-namespace tests {
-namespace permission {
-
-BpProtectedInterface::BpProtectedInterface(const ::android::sp<::android::IBinder>& _aidl_impl)
-    : BpInterface<IProtectedInterface>(_aidl_impl){
-}
-
-::android::binder::Status BpProtectedInterface::Method1() {
-  ::android::Parcel _aidl_data;
-  _aidl_data.markForBinder(remoteStrong());
-  ::android::Parcel _aidl_reply;
-  ::android::status_t _aidl_ret_status = ::android::OK;
-  ::android::binder::Status _aidl_status;
-  _aidl_ret_status = _aidl_data.writeInterfaceToken(getInterfaceDescriptor());
-  if (((_aidl_ret_status) != (::android::OK))) {
-    goto _aidl_error;
-  }
-  _aidl_ret_status = remote()->transact(BnProtectedInterface::TRANSACTION_Method1, _aidl_data, &_aidl_reply, 0);
-  if (UNLIKELY(_aidl_ret_status == ::android::UNKNOWN_TRANSACTION && IProtectedInterface::getDefaultImpl())) {
-     return IProtectedInterface::getDefaultImpl()->Method1();
-  }
-  if (((_aidl_ret_status) != (::android::OK))) {
-    goto _aidl_error;
-  }
-  _aidl_ret_status = _aidl_status.readFromParcel(_aidl_reply);
-  if (((_aidl_ret_status) != (::android::OK))) {
-    goto _aidl_error;
-  }
-  if (!_aidl_status.isOk()) {
-    return _aidl_status;
-  }
-  _aidl_error:
-  _aidl_status.setFromStatusT(_aidl_ret_status);
-  return _aidl_status;
-}
-
-::android::binder::Status BpProtectedInterface::Method2() {
-  ::android::Parcel _aidl_data;
-  _aidl_data.markForBinder(remoteStrong());
-  ::android::Parcel _aidl_reply;
-  ::android::status_t _aidl_ret_status = ::android::OK;
-  ::android::binder::Status _aidl_status;
-  _aidl_ret_status = _aidl_data.writeInterfaceToken(getInterfaceDescriptor());
-  if (((_aidl_ret_status) != (::android::OK))) {
-    goto _aidl_error;
-  }
-  _aidl_ret_status = remote()->transact(BnProtectedInterface::TRANSACTION_Method2, _aidl_data, &_aidl_reply, 0);
-  if (UNLIKELY(_aidl_ret_status == ::android::UNKNOWN_TRANSACTION && IProtectedInterface::getDefaultImpl())) {
-     return IProtectedInterface::getDefaultImpl()->Method2();
-  }
-  if (((_aidl_ret_status) != (::android::OK))) {
-    goto _aidl_error;
-  }
-  _aidl_ret_status = _aidl_status.readFromParcel(_aidl_reply);
-  if (((_aidl_ret_status) != (::android::OK))) {
-    goto _aidl_error;
-  }
-  if (!_aidl_status.isOk()) {
-    return _aidl_status;
-  }
-  _aidl_error:
-  _aidl_status.setFromStatusT(_aidl_ret_status);
-  return _aidl_status;
-}
-
-}  // namespace permission
-}  // namespace tests
-}  // namespace aidl
-}  // namespace android
-#include <android/aidl/tests/permission/BnProtectedInterface.h>
-#include <binder/Parcel.h>
-#include <binder/Stability.h>
-
-namespace android {
-namespace aidl {
-namespace tests {
-namespace permission {
-
-BnProtectedInterface::BnProtectedInterface()
-{
-  ::android::internal::Stability::markCompilationUnit(this);
-}
-
-::android::status_t BnProtectedInterface::onTransact(uint32_t _aidl_code, const ::android::Parcel& _aidl_data, ::android::Parcel* _aidl_reply, uint32_t _aidl_flags) {
-  ::android::status_t _aidl_ret_status = ::android::OK;
-  switch (_aidl_code) {
-  case BnProtectedInterface::TRANSACTION_Method1:
-  {
-    if (!(_aidl_data.checkInterface(this))) {
-      _aidl_ret_status = ::android::BAD_TYPE;
-      break;
-    }
-    ::android::binder::Status _aidl_status(Method1());
-    _aidl_ret_status = _aidl_status.writeToParcel(_aidl_reply);
-    if (((_aidl_ret_status) != (::android::OK))) {
-      break;
-    }
-    if (!_aidl_status.isOk()) {
-      break;
-    }
-  }
-  break;
-  case BnProtectedInterface::TRANSACTION_Method2:
-  {
-    if (!(_aidl_data.checkInterface(this))) {
-      _aidl_ret_status = ::android::BAD_TYPE;
-      break;
-    }
-    ::android::binder::Status _aidl_status(Method2());
-    _aidl_ret_status = _aidl_status.writeToParcel(_aidl_reply);
-    if (((_aidl_ret_status) != (::android::OK))) {
-      break;
-    }
-    if (!_aidl_status.isOk()) {
-      break;
-    }
-  }
-  break;
-  default:
-  {
-    _aidl_ret_status = ::android::BBinder::onTransact(_aidl_code, _aidl_data, _aidl_reply, _aidl_flags);
-  }
-  break;
-  }
-  if (_aidl_ret_status == ::android::UNEXPECTED_NULL) {
-    _aidl_ret_status = ::android::binder::Status::fromExceptionCode(::android::binder::Status::EX_NULL_POINTER).writeOverParcel(_aidl_reply);
-  }
-  return _aidl_ret_status;
-}
-
-}  // namespace permission
-}  // namespace tests
-}  // namespace aidl
-}  // namespace android
diff --git a/tests/golden_output/aidl-test-interface-cpp-source/gen/android/aidl/tests/permission/IProtectedInterface.cpp.d b/tests/golden_output/aidl-test-interface-cpp-source/gen/android/aidl/tests/permission/IProtectedInterface.cpp.d
deleted file mode 100644
index 1841b65..0000000
--- a/tests/golden_output/aidl-test-interface-cpp-source/gen/android/aidl/tests/permission/IProtectedInterface.cpp.d
+++ /dev/null
@@ -1,2 +0,0 @@
-out/soong/.intermediates/system/tools/aidl/aidl-test-interface-cpp-source/gen/android/aidl/tests/permission/IProtectedInterface.cpp : \
-  system/tools/aidl/tests/android/aidl/tests/permission/IProtectedInterface.aidl
diff --git a/tests/golden_output/aidl-test-interface-cpp-source/gen/android/aidl/tests/unions/EnumUnion.cpp b/tests/golden_output/aidl-test-interface-cpp-source/gen/android/aidl/tests/unions/EnumUnion.cpp
index e780a45..0261f25 100644
--- a/tests/golden_output/aidl-test-interface-cpp-source/gen/android/aidl/tests/unions/EnumUnion.cpp
+++ b/tests/golden_output/aidl-test-interface-cpp-source/gen/android/aidl/tests/unions/EnumUnion.cpp
@@ -8,7 +8,7 @@
   ::android::status_t _aidl_ret_status;
   int32_t _aidl_tag;
   if ((_aidl_ret_status = _aidl_parcel->readInt32(&_aidl_tag)) != ::android::OK) return _aidl_ret_status;
-  switch (_aidl_tag) {
+  switch (static_cast<Tag>(_aidl_tag)) {
   case intEnum: {
     ::android::aidl::tests::IntEnum _aidl_value;
     if ((_aidl_ret_status = _aidl_parcel->readInt32(reinterpret_cast<int32_t *>(&_aidl_value))) != ::android::OK) return _aidl_ret_status;
@@ -29,15 +29,32 @@
       set<longEnum>(std::move(_aidl_value));
     }
     return ::android::OK; }
+  #pragma clang diagnostic push
+  #pragma clang diagnostic ignored "-Wdeprecated-declarations"
+  case deprecatedField: {
+    int32_t _aidl_value;
+    if ((_aidl_ret_status = _aidl_parcel->readInt32(&_aidl_value)) != ::android::OK) return _aidl_ret_status;
+    if constexpr (std::is_trivially_copyable_v<int32_t>) {
+      set<deprecatedField>(_aidl_value);
+    } else {
+      // NOLINTNEXTLINE(performance-move-const-arg)
+      set<deprecatedField>(std::move(_aidl_value));
+    }
+    return ::android::OK; }
+  #pragma clang diagnostic pop
   }
   return ::android::BAD_VALUE;
 }
 ::android::status_t EnumUnion::writeToParcel(::android::Parcel* _aidl_parcel) const {
-  ::android::status_t _aidl_ret_status = _aidl_parcel->writeInt32(getTag());
+  ::android::status_t _aidl_ret_status = _aidl_parcel->writeInt32(static_cast<int32_t>(getTag()));
   if (_aidl_ret_status != ::android::OK) return _aidl_ret_status;
   switch (getTag()) {
   case intEnum: return _aidl_parcel->writeInt32(static_cast<int32_t>(get<intEnum>()));
   case longEnum: return _aidl_parcel->writeInt64(static_cast<int64_t>(get<longEnum>()));
+  #pragma clang diagnostic push
+  #pragma clang diagnostic ignored "-Wdeprecated-declarations"
+  case deprecatedField: return _aidl_parcel->writeInt32(get<deprecatedField>());
+  #pragma clang diagnostic pop
   }
   __assert2(__FILE__, __LINE__, __PRETTY_FUNCTION__, "can't reach here");
 }
diff --git a/tests/golden_output/aidl-test-interface-cpp-source/gen/android/aidl/tests/unions/UnionInUnion.cpp b/tests/golden_output/aidl-test-interface-cpp-source/gen/android/aidl/tests/unions/UnionInUnion.cpp
index 06f668e..100dda2 100644
--- a/tests/golden_output/aidl-test-interface-cpp-source/gen/android/aidl/tests/unions/UnionInUnion.cpp
+++ b/tests/golden_output/aidl-test-interface-cpp-source/gen/android/aidl/tests/unions/UnionInUnion.cpp
@@ -8,7 +8,7 @@
   ::android::status_t _aidl_ret_status;
   int32_t _aidl_tag;
   if ((_aidl_ret_status = _aidl_parcel->readInt32(&_aidl_tag)) != ::android::OK) return _aidl_ret_status;
-  switch (_aidl_tag) {
+  switch (static_cast<Tag>(_aidl_tag)) {
   case first: {
     ::android::aidl::tests::unions::EnumUnion _aidl_value;
     if ((_aidl_ret_status = _aidl_parcel->readParcelable(&_aidl_value)) != ::android::OK) return _aidl_ret_status;
@@ -33,7 +33,7 @@
   return ::android::BAD_VALUE;
 }
 ::android::status_t UnionInUnion::writeToParcel(::android::Parcel* _aidl_parcel) const {
-  ::android::status_t _aidl_ret_status = _aidl_parcel->writeInt32(getTag());
+  ::android::status_t _aidl_ret_status = _aidl_parcel->writeInt32(static_cast<int32_t>(getTag()));
   if (_aidl_ret_status != ::android::OK) return _aidl_ret_status;
   switch (getTag()) {
   case first: return _aidl_parcel->writeParcelable(get<first>());
diff --git a/tests/golden_output/aidl-test-interface-cpp-source/gen/include/android/aidl/tests/ArrayOfInterfaces.h b/tests/golden_output/aidl-test-interface-cpp-source/gen/include/android/aidl/tests/ArrayOfInterfaces.h
index 6932199..b5c0a6c 100644
--- a/tests/golden_output/aidl-test-interface-cpp-source/gen/include/android/aidl/tests/ArrayOfInterfaces.h
+++ b/tests/golden_output/aidl-test-interface-cpp-source/gen/include/android/aidl/tests/ArrayOfInterfaces.h
@@ -2,12 +2,16 @@
 
 #include <android/aidl/tests/ArrayOfInterfaces.h>
 #include <android/binder_to_string.h>
+#include <array>
+#include <binder/Enums.h>
 #include <binder/IBinder.h>
 #include <binder/IInterface.h>
 #include <binder/Parcel.h>
 #include <binder/Status.h>
 #include <cassert>
+#include <cstdint>
 #include <optional>
+#include <string>
 #include <tuple>
 #include <type_traits>
 #include <utility>
@@ -136,17 +140,22 @@
   };  // class MyParcelable
   class MyUnion : public ::android::Parcelable {
   public:
-    enum Tag : int32_t {
-      iface = 0,  // android.aidl.tests.ArrayOfInterfaces.IEmptyInterface iface;
-      nullable_iface,  // android.aidl.tests.ArrayOfInterfaces.IEmptyInterface nullable_iface;
-      iface_array,  // android.aidl.tests.ArrayOfInterfaces.IEmptyInterface[] iface_array;
-      nullable_iface_array,  // android.aidl.tests.ArrayOfInterfaces.IEmptyInterface[] nullable_iface_array;
+    enum class Tag : int32_t {
+      iface = 0,
+      nullable_iface = 1,
+      iface_array = 2,
+      nullable_iface_array = 3,
     };
+    // Expose tag symbols for legacy code
+    static const inline Tag iface = Tag::iface;
+    static const inline Tag nullable_iface = Tag::nullable_iface;
+    static const inline Tag iface_array = Tag::iface_array;
+    static const inline Tag nullable_iface_array = Tag::nullable_iface_array;
 
     template<typename _Tp>
     static constexpr bool _not_self = !std::is_same_v<std::remove_cv_t<std::remove_reference_t<_Tp>>, MyUnion>;
 
-    MyUnion() : _value(std::in_place_index<iface>, ::android::sp<::android::aidl::tests::ArrayOfInterfaces::IEmptyInterface>()) { }
+    MyUnion() : _value(std::in_place_index<static_cast<size_t>(iface)>, ::android::sp<::android::aidl::tests::ArrayOfInterfaces::IEmptyInterface>()) { }
 
     template <typename _Tp, typename = std::enable_if_t<_not_self<_Tp>>>
     // NOLINTNEXTLINE(google-explicit-constructor)
@@ -159,12 +168,12 @@
 
     template <Tag _tag, typename... _Tp>
     static MyUnion make(_Tp&&... _args) {
-      return MyUnion(std::in_place_index<_tag>, std::forward<_Tp>(_args)...);
+      return MyUnion(std::in_place_index<static_cast<size_t>(_tag)>, std::forward<_Tp>(_args)...);
     }
 
     template <Tag _tag, typename _Tp, typename... _Up>
     static MyUnion make(std::initializer_list<_Tp> _il, _Up&&... _args) {
-      return MyUnion(std::in_place_index<_tag>, std::move(_il), std::forward<_Up>(_args)...);
+      return MyUnion(std::in_place_index<static_cast<size_t>(_tag)>, std::move(_il), std::forward<_Up>(_args)...);
     }
 
     Tag getTag() const {
@@ -174,18 +183,18 @@
     template <Tag _tag>
     const auto& get() const {
       if (getTag() != _tag) { __assert2(__FILE__, __LINE__, __PRETTY_FUNCTION__, "bad access: a wrong tag"); }
-      return std::get<_tag>(_value);
+      return std::get<static_cast<size_t>(_tag)>(_value);
     }
 
     template <Tag _tag>
     auto& get() {
       if (getTag() != _tag) { __assert2(__FILE__, __LINE__, __PRETTY_FUNCTION__, "bad access: a wrong tag"); }
-      return std::get<_tag>(_value);
+      return std::get<static_cast<size_t>(_tag)>(_value);
     }
 
     template <Tag _tag, typename... _Tp>
     void set(_Tp&&... _args) {
-      _value.emplace<_tag>(std::forward<_Tp>(_args)...);
+      _value.emplace<static_cast<size_t>(_tag)>(std::forward<_Tp>(_args)...);
     }
 
     inline bool operator!=(const MyUnion& rhs) const {
@@ -263,3 +272,37 @@
 }  // namespace tests
 }  // namespace aidl
 }  // namespace android
+namespace android {
+namespace aidl {
+namespace tests {
+[[nodiscard]] static inline std::string toString(ArrayOfInterfaces::MyUnion::Tag val) {
+  switch(val) {
+  case ArrayOfInterfaces::MyUnion::Tag::iface:
+    return "iface";
+  case ArrayOfInterfaces::MyUnion::Tag::nullable_iface:
+    return "nullable_iface";
+  case ArrayOfInterfaces::MyUnion::Tag::iface_array:
+    return "iface_array";
+  case ArrayOfInterfaces::MyUnion::Tag::nullable_iface_array:
+    return "nullable_iface_array";
+  default:
+    return std::to_string(static_cast<int32_t>(val));
+  }
+}
+}  // namespace tests
+}  // namespace aidl
+}  // namespace android
+namespace android {
+namespace internal {
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wc++17-extensions"
+template <>
+constexpr inline std::array<::android::aidl::tests::ArrayOfInterfaces::MyUnion::Tag, 4> enum_values<::android::aidl::tests::ArrayOfInterfaces::MyUnion::Tag> = {
+  ::android::aidl::tests::ArrayOfInterfaces::MyUnion::Tag::iface,
+  ::android::aidl::tests::ArrayOfInterfaces::MyUnion::Tag::nullable_iface,
+  ::android::aidl::tests::ArrayOfInterfaces::MyUnion::Tag::iface_array,
+  ::android::aidl::tests::ArrayOfInterfaces::MyUnion::Tag::nullable_iface_array,
+};
+#pragma clang diagnostic pop
+}  // namespace internal
+}  // namespace android
diff --git a/tests/golden_output/aidl-test-interface-cpp-source/gen/include/android/aidl/tests/BnTestService.h b/tests/golden_output/aidl-test-interface-cpp-source/gen/include/android/aidl/tests/BnTestService.h
index 2d2a864..3cb8ad0 100644
--- a/tests/golden_output/aidl-test-interface-cpp-source/gen/include/android/aidl/tests/BnTestService.h
+++ b/tests/golden_output/aidl-test-interface-cpp-source/gen/include/android/aidl/tests/BnTestService.h
@@ -71,8 +71,9 @@
   static constexpr uint32_t TRANSACTION_ReverseNullableIBinderArray = ::android::IBinder::FIRST_CALL_TRANSACTION + 60;
   static constexpr uint32_t TRANSACTION_GetOldNameInterface = ::android::IBinder::FIRST_CALL_TRANSACTION + 61;
   static constexpr uint32_t TRANSACTION_GetNewNameInterface = ::android::IBinder::FIRST_CALL_TRANSACTION + 62;
-  static constexpr uint32_t TRANSACTION_GetCppJavaTests = ::android::IBinder::FIRST_CALL_TRANSACTION + 63;
-  static constexpr uint32_t TRANSACTION_getBackendType = ::android::IBinder::FIRST_CALL_TRANSACTION + 64;
+  static constexpr uint32_t TRANSACTION_GetUnionTags = ::android::IBinder::FIRST_CALL_TRANSACTION + 63;
+  static constexpr uint32_t TRANSACTION_GetCppJavaTests = ::android::IBinder::FIRST_CALL_TRANSACTION + 64;
+  static constexpr uint32_t TRANSACTION_getBackendType = ::android::IBinder::FIRST_CALL_TRANSACTION + 65;
   explicit BnTestService();
   ::android::status_t onTransact(uint32_t _aidl_code, const ::android::Parcel& _aidl_data, ::android::Parcel* _aidl_reply, uint32_t _aidl_flags) override;
 };  // class BnTestService
@@ -270,6 +271,9 @@
   ::android::binder::Status GetNewNameInterface(::android::sp<::android::aidl::tests::INewName>* _aidl_return) override {
     return _aidl_delegate->GetNewNameInterface(_aidl_return);
   }
+  ::android::binder::Status GetUnionTags(const ::std::vector<::android::aidl::tests::Union>& input, ::std::vector<::android::aidl::tests::Union::Tag>* _aidl_return) override {
+    return _aidl_delegate->GetUnionTags(input, _aidl_return);
+  }
   ::android::binder::Status GetCppJavaTests(::android::sp<::android::IBinder>* _aidl_return) override {
     return _aidl_delegate->GetCppJavaTests(_aidl_return);
   }
diff --git a/tests/golden_output/aidl-test-interface-cpp-source/gen/include/android/aidl/tests/BpTestService.h b/tests/golden_output/aidl-test-interface-cpp-source/gen/include/android/aidl/tests/BpTestService.h
index c1cbaa8..3829d9b 100644
--- a/tests/golden_output/aidl-test-interface-cpp-source/gen/include/android/aidl/tests/BpTestService.h
+++ b/tests/golden_output/aidl-test-interface-cpp-source/gen/include/android/aidl/tests/BpTestService.h
@@ -75,6 +75,7 @@
   ::android::binder::Status ReverseNullableIBinderArray(const ::std::optional<::std::vector<::android::sp<::android::IBinder>>>& input, ::std::optional<::std::vector<::android::sp<::android::IBinder>>>* repeated, ::std::optional<::std::vector<::android::sp<::android::IBinder>>>* _aidl_return) override;
   ::android::binder::Status GetOldNameInterface(::android::sp<::android::aidl::tests::IOldName>* _aidl_return) override;
   ::android::binder::Status GetNewNameInterface(::android::sp<::android::aidl::tests::INewName>* _aidl_return) override;
+  ::android::binder::Status GetUnionTags(const ::std::vector<::android::aidl::tests::Union>& input, ::std::vector<::android::aidl::tests::Union::Tag>* _aidl_return) override;
   ::android::binder::Status GetCppJavaTests(::android::sp<::android::IBinder>* _aidl_return) override;
   ::android::binder::Status getBackendType(::android::aidl::tests::BackendType* _aidl_return) override;
 };  // class BpTestService
diff --git a/tests/golden_output/aidl-test-interface-cpp-source/gen/include/android/aidl/tests/DeprecatedEnum.h b/tests/golden_output/aidl-test-interface-cpp-source/gen/include/android/aidl/tests/DeprecatedEnum.h
index c89b38f..593eaea 100644
--- a/tests/golden_output/aidl-test-interface-cpp-source/gen/include/android/aidl/tests/DeprecatedEnum.h
+++ b/tests/golden_output/aidl-test-interface-cpp-source/gen/include/android/aidl/tests/DeprecatedEnum.h
@@ -19,7 +19,8 @@
 namespace android {
 namespace aidl {
 namespace tests {
-[[nodiscard]] static inline std::string toString(DeprecatedEnum val) __attribute__((deprecated("test")));
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wdeprecated-declarations"
 [[nodiscard]] static inline std::string toString(DeprecatedEnum val) {
   switch(val) {
   case DeprecatedEnum::A:
@@ -32,6 +33,7 @@
     return std::to_string(static_cast<int32_t>(val));
   }
 }
+#pragma clang diagnostic pop
 }  // namespace tests
 }  // namespace aidl
 }  // namespace android
@@ -39,8 +41,9 @@
 namespace internal {
 #pragma clang diagnostic push
 #pragma clang diagnostic ignored "-Wc++17-extensions"
+#pragma clang diagnostic ignored "-Wdeprecated-declarations"
 template <>
-constexpr inline std::array<::android::aidl::tests::DeprecatedEnum, 3> __attribute__((deprecated("test"))) enum_values<::android::aidl::tests::DeprecatedEnum> = {
+constexpr inline std::array<::android::aidl::tests::DeprecatedEnum, 3> enum_values<::android::aidl::tests::DeprecatedEnum> = {
   ::android::aidl::tests::DeprecatedEnum::A,
   ::android::aidl::tests::DeprecatedEnum::B,
   ::android::aidl::tests::DeprecatedEnum::C,
diff --git a/tests/golden_output/aidl-test-interface-cpp-source/gen/include/android/aidl/tests/FixedSize.h b/tests/golden_output/aidl-test-interface-cpp-source/gen/include/android/aidl/tests/FixedSize.h
index 2e670e3..65f6711 100644
--- a/tests/golden_output/aidl-test-interface-cpp-source/gen/include/android/aidl/tests/FixedSize.h
+++ b/tests/golden_output/aidl-test-interface-cpp-source/gen/include/android/aidl/tests/FixedSize.h
@@ -3,10 +3,13 @@
 #include <android/aidl/tests/FixedSize.h>
 #include <android/aidl/tests/LongEnum.h>
 #include <android/binder_to_string.h>
+#include <array>
+#include <binder/Enums.h>
 #include <binder/Parcel.h>
 #include <binder/Status.h>
 #include <cassert>
 #include <cstdint>
+#include <string>
 #include <tuple>
 #include <type_traits>
 #include <utility>
@@ -24,19 +27,28 @@
 public:
   class FixedUnion : public ::android::Parcelable {
   public:
-    enum Tag : uint8_t {
-      booleanValue = 0,  // boolean booleanValue;
-      byteValue,  // byte byteValue;
-      charValue,  // char charValue;
-      intValue,  // int intValue;
-      longValue,  // long longValue;
-      floatValue,  // float floatValue;
-      doubleValue,  // double doubleValue;
-      enumValue,  // android.aidl.tests.LongEnum enumValue;
+    enum class Tag : int8_t {
+      booleanValue = 0,
+      byteValue = 1,
+      charValue = 2,
+      intValue = 3,
+      longValue = 4,
+      floatValue = 5,
+      doubleValue = 6,
+      enumValue = 7,
     };
+    // Expose tag symbols for legacy code
+    static const inline Tag booleanValue = Tag::booleanValue;
+    static const inline Tag byteValue = Tag::byteValue;
+    static const inline Tag charValue = Tag::charValue;
+    static const inline Tag intValue = Tag::intValue;
+    static const inline Tag longValue = Tag::longValue;
+    static const inline Tag floatValue = Tag::floatValue;
+    static const inline Tag doubleValue = Tag::doubleValue;
+    static const inline Tag enumValue = Tag::enumValue;
 
     template <Tag _Tag>
-    using _at = typename std::tuple_element<_Tag, std::tuple<bool, int8_t, char16_t, int32_t, int64_t, float, double, ::android::aidl::tests::LongEnum>>::type;
+    using _at = typename std::tuple_element<static_cast<size_t>(_Tag), std::tuple<bool, int8_t, char16_t, int32_t, int64_t, float, double, ::android::aidl::tests::LongEnum>>::type;
     template <Tag _Tag, typename _Type>
     static FixedUnion make(_Type&& _arg) {
       FixedUnion _inst;
@@ -71,7 +83,7 @@
       } else {
         return (_lhs.getTag() == _Tag)
           ? _cmp_value(_lhs.get<_Tag>(), _rhs.get<_Tag>())
-          : _cmp_value_at<(Tag)(_Tag-1)>(_lhs, _rhs);
+          : _cmp_value_at<static_cast<Tag>(static_cast<size_t>(_Tag)-1)>(_lhs, _rhs);
       }
     }
     template <typename _Type>
@@ -119,7 +131,7 @@
       return os.str();
     }
   private:
-    Tag _tag __attribute__((aligned (1))) = booleanValue;
+    Tag _tag = booleanValue;
     union _value_t {
       _value_t() {}
       ~_value_t() {}
@@ -220,3 +232,49 @@
 }  // namespace tests
 }  // namespace aidl
 }  // namespace android
+namespace android {
+namespace aidl {
+namespace tests {
+[[nodiscard]] static inline std::string toString(FixedSize::FixedUnion::Tag val) {
+  switch(val) {
+  case FixedSize::FixedUnion::Tag::booleanValue:
+    return "booleanValue";
+  case FixedSize::FixedUnion::Tag::byteValue:
+    return "byteValue";
+  case FixedSize::FixedUnion::Tag::charValue:
+    return "charValue";
+  case FixedSize::FixedUnion::Tag::intValue:
+    return "intValue";
+  case FixedSize::FixedUnion::Tag::longValue:
+    return "longValue";
+  case FixedSize::FixedUnion::Tag::floatValue:
+    return "floatValue";
+  case FixedSize::FixedUnion::Tag::doubleValue:
+    return "doubleValue";
+  case FixedSize::FixedUnion::Tag::enumValue:
+    return "enumValue";
+  default:
+    return std::to_string(static_cast<int8_t>(val));
+  }
+}
+}  // namespace tests
+}  // namespace aidl
+}  // namespace android
+namespace android {
+namespace internal {
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wc++17-extensions"
+template <>
+constexpr inline std::array<::android::aidl::tests::FixedSize::FixedUnion::Tag, 8> enum_values<::android::aidl::tests::FixedSize::FixedUnion::Tag> = {
+  ::android::aidl::tests::FixedSize::FixedUnion::Tag::booleanValue,
+  ::android::aidl::tests::FixedSize::FixedUnion::Tag::byteValue,
+  ::android::aidl::tests::FixedSize::FixedUnion::Tag::charValue,
+  ::android::aidl::tests::FixedSize::FixedUnion::Tag::intValue,
+  ::android::aidl::tests::FixedSize::FixedUnion::Tag::longValue,
+  ::android::aidl::tests::FixedSize::FixedUnion::Tag::floatValue,
+  ::android::aidl::tests::FixedSize::FixedUnion::Tag::doubleValue,
+  ::android::aidl::tests::FixedSize::FixedUnion::Tag::enumValue,
+};
+#pragma clang diagnostic pop
+}  // namespace internal
+}  // namespace android
diff --git a/tests/golden_output/aidl-test-interface-cpp-source/gen/include/android/aidl/tests/ITestService.h b/tests/golden_output/aidl-test-interface-cpp-source/gen/include/android/aidl/tests/ITestService.h
index dd0e7f1..9246a87 100644
--- a/tests/golden_output/aidl-test-interface-cpp-source/gen/include/android/aidl/tests/ITestService.h
+++ b/tests/golden_output/aidl-test-interface-cpp-source/gen/include/android/aidl/tests/ITestService.h
@@ -10,6 +10,7 @@
 #include <android/aidl/tests/LongEnum.h>
 #include <android/aidl/tests/RecursiveList.h>
 #include <android/aidl/tests/StructuredParcelable.h>
+#include <android/aidl/tests/Union.h>
 #include <android/aidl/tests/extension/ExtendableParcelable.h>
 #include <android/binder_to_string.h>
 #include <binder/IBinder.h>
@@ -272,6 +273,7 @@
   virtual ::android::binder::Status ReverseNullableIBinderArray(const ::std::optional<::std::vector<::android::sp<::android::IBinder>>>& input, ::std::optional<::std::vector<::android::sp<::android::IBinder>>>* repeated, ::std::optional<::std::vector<::android::sp<::android::IBinder>>>* _aidl_return) = 0;
   virtual ::android::binder::Status GetOldNameInterface(::android::sp<::android::aidl::tests::IOldName>* _aidl_return) = 0;
   virtual ::android::binder::Status GetNewNameInterface(::android::sp<::android::aidl::tests::INewName>* _aidl_return) = 0;
+  virtual ::android::binder::Status GetUnionTags(const ::std::vector<::android::aidl::tests::Union>& input, ::std::vector<::android::aidl::tests::Union::Tag>* _aidl_return) = 0;
   virtual ::android::binder::Status GetCppJavaTests(::android::sp<::android::IBinder>* _aidl_return) = 0;
   virtual ::android::binder::Status getBackendType(::android::aidl::tests::BackendType* _aidl_return) = 0;
 };  // class ITestService
@@ -470,6 +472,9 @@
   ::android::binder::Status GetNewNameInterface(::android::sp<::android::aidl::tests::INewName>* /*_aidl_return*/) override {
     return ::android::binder::Status::fromStatusT(::android::UNKNOWN_TRANSACTION);
   }
+  ::android::binder::Status GetUnionTags(const ::std::vector<::android::aidl::tests::Union>& /*input*/, ::std::vector<::android::aidl::tests::Union::Tag>* /*_aidl_return*/) override {
+    return ::android::binder::Status::fromStatusT(::android::UNKNOWN_TRANSACTION);
+  }
   ::android::binder::Status GetCppJavaTests(::android::sp<::android::IBinder>* /*_aidl_return*/) override {
     return ::android::binder::Status::fromStatusT(::android::UNKNOWN_TRANSACTION);
   }
diff --git a/tests/golden_output/aidl-test-interface-cpp-source/gen/include/android/aidl/tests/IntEnum.h b/tests/golden_output/aidl-test-interface-cpp-source/gen/include/android/aidl/tests/IntEnum.h
index 00e8738..2e57678 100644
--- a/tests/golden_output/aidl-test-interface-cpp-source/gen/include/android/aidl/tests/IntEnum.h
+++ b/tests/golden_output/aidl-test-interface-cpp-source/gen/include/android/aidl/tests/IntEnum.h
@@ -12,6 +12,7 @@
   FOO = 1000,
   BAR = 2000,
   BAZ = 2001,
+  QUX __attribute__((deprecated("do not use this"))) = 2002,
 };
 }  // namespace tests
 }  // namespace aidl
@@ -19,6 +20,8 @@
 namespace android {
 namespace aidl {
 namespace tests {
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wdeprecated-declarations"
 [[nodiscard]] static inline std::string toString(IntEnum val) {
   switch(val) {
   case IntEnum::FOO:
@@ -27,10 +30,13 @@
     return "BAR";
   case IntEnum::BAZ:
     return "BAZ";
+  case IntEnum::QUX:
+    return "QUX";
   default:
     return std::to_string(static_cast<int32_t>(val));
   }
 }
+#pragma clang diagnostic pop
 }  // namespace tests
 }  // namespace aidl
 }  // namespace android
@@ -38,11 +44,13 @@
 namespace internal {
 #pragma clang diagnostic push
 #pragma clang diagnostic ignored "-Wc++17-extensions"
+#pragma clang diagnostic ignored "-Wdeprecated-declarations"
 template <>
-constexpr inline std::array<::android::aidl::tests::IntEnum, 3> enum_values<::android::aidl::tests::IntEnum> = {
+constexpr inline std::array<::android::aidl::tests::IntEnum, 4> enum_values<::android::aidl::tests::IntEnum> = {
   ::android::aidl::tests::IntEnum::FOO,
   ::android::aidl::tests::IntEnum::BAR,
   ::android::aidl::tests::IntEnum::BAZ,
+  ::android::aidl::tests::IntEnum::QUX,
 };
 #pragma clang diagnostic pop
 }  // namespace internal
diff --git a/tests/golden_output/aidl-test-interface-cpp-source/gen/include/android/aidl/tests/ListOfInterfaces.h b/tests/golden_output/aidl-test-interface-cpp-source/gen/include/android/aidl/tests/ListOfInterfaces.h
index 696c816..455bf4b 100644
--- a/tests/golden_output/aidl-test-interface-cpp-source/gen/include/android/aidl/tests/ListOfInterfaces.h
+++ b/tests/golden_output/aidl-test-interface-cpp-source/gen/include/android/aidl/tests/ListOfInterfaces.h
@@ -2,12 +2,16 @@
 
 #include <android/aidl/tests/ListOfInterfaces.h>
 #include <android/binder_to_string.h>
+#include <array>
+#include <binder/Enums.h>
 #include <binder/IBinder.h>
 #include <binder/IInterface.h>
 #include <binder/Parcel.h>
 #include <binder/Status.h>
 #include <cassert>
+#include <cstdint>
 #include <optional>
+#include <string>
 #include <tuple>
 #include <type_traits>
 #include <utility>
@@ -136,17 +140,22 @@
   };  // class MyParcelable
   class MyUnion : public ::android::Parcelable {
   public:
-    enum Tag : int32_t {
-      iface = 0,  // android.aidl.tests.ListOfInterfaces.IEmptyInterface iface;
-      nullable_iface,  // android.aidl.tests.ListOfInterfaces.IEmptyInterface nullable_iface;
-      iface_list,  // List<android.aidl.tests.ListOfInterfaces.IEmptyInterface> iface_list;
-      nullable_iface_list,  // List<android.aidl.tests.ListOfInterfaces.IEmptyInterface> nullable_iface_list;
+    enum class Tag : int32_t {
+      iface = 0,
+      nullable_iface = 1,
+      iface_list = 2,
+      nullable_iface_list = 3,
     };
+    // Expose tag symbols for legacy code
+    static const inline Tag iface = Tag::iface;
+    static const inline Tag nullable_iface = Tag::nullable_iface;
+    static const inline Tag iface_list = Tag::iface_list;
+    static const inline Tag nullable_iface_list = Tag::nullable_iface_list;
 
     template<typename _Tp>
     static constexpr bool _not_self = !std::is_same_v<std::remove_cv_t<std::remove_reference_t<_Tp>>, MyUnion>;
 
-    MyUnion() : _value(std::in_place_index<iface>, ::android::sp<::android::aidl::tests::ListOfInterfaces::IEmptyInterface>()) { }
+    MyUnion() : _value(std::in_place_index<static_cast<size_t>(iface)>, ::android::sp<::android::aidl::tests::ListOfInterfaces::IEmptyInterface>()) { }
 
     template <typename _Tp, typename = std::enable_if_t<_not_self<_Tp>>>
     // NOLINTNEXTLINE(google-explicit-constructor)
@@ -159,12 +168,12 @@
 
     template <Tag _tag, typename... _Tp>
     static MyUnion make(_Tp&&... _args) {
-      return MyUnion(std::in_place_index<_tag>, std::forward<_Tp>(_args)...);
+      return MyUnion(std::in_place_index<static_cast<size_t>(_tag)>, std::forward<_Tp>(_args)...);
     }
 
     template <Tag _tag, typename _Tp, typename... _Up>
     static MyUnion make(std::initializer_list<_Tp> _il, _Up&&... _args) {
-      return MyUnion(std::in_place_index<_tag>, std::move(_il), std::forward<_Up>(_args)...);
+      return MyUnion(std::in_place_index<static_cast<size_t>(_tag)>, std::move(_il), std::forward<_Up>(_args)...);
     }
 
     Tag getTag() const {
@@ -174,18 +183,18 @@
     template <Tag _tag>
     const auto& get() const {
       if (getTag() != _tag) { __assert2(__FILE__, __LINE__, __PRETTY_FUNCTION__, "bad access: a wrong tag"); }
-      return std::get<_tag>(_value);
+      return std::get<static_cast<size_t>(_tag)>(_value);
     }
 
     template <Tag _tag>
     auto& get() {
       if (getTag() != _tag) { __assert2(__FILE__, __LINE__, __PRETTY_FUNCTION__, "bad access: a wrong tag"); }
-      return std::get<_tag>(_value);
+      return std::get<static_cast<size_t>(_tag)>(_value);
     }
 
     template <Tag _tag, typename... _Tp>
     void set(_Tp&&... _args) {
-      _value.emplace<_tag>(std::forward<_Tp>(_args)...);
+      _value.emplace<static_cast<size_t>(_tag)>(std::forward<_Tp>(_args)...);
     }
 
     inline bool operator!=(const MyUnion& rhs) const {
@@ -263,3 +272,37 @@
 }  // namespace tests
 }  // namespace aidl
 }  // namespace android
+namespace android {
+namespace aidl {
+namespace tests {
+[[nodiscard]] static inline std::string toString(ListOfInterfaces::MyUnion::Tag val) {
+  switch(val) {
+  case ListOfInterfaces::MyUnion::Tag::iface:
+    return "iface";
+  case ListOfInterfaces::MyUnion::Tag::nullable_iface:
+    return "nullable_iface";
+  case ListOfInterfaces::MyUnion::Tag::iface_list:
+    return "iface_list";
+  case ListOfInterfaces::MyUnion::Tag::nullable_iface_list:
+    return "nullable_iface_list";
+  default:
+    return std::to_string(static_cast<int32_t>(val));
+  }
+}
+}  // namespace tests
+}  // namespace aidl
+}  // namespace android
+namespace android {
+namespace internal {
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wc++17-extensions"
+template <>
+constexpr inline std::array<::android::aidl::tests::ListOfInterfaces::MyUnion::Tag, 4> enum_values<::android::aidl::tests::ListOfInterfaces::MyUnion::Tag> = {
+  ::android::aidl::tests::ListOfInterfaces::MyUnion::Tag::iface,
+  ::android::aidl::tests::ListOfInterfaces::MyUnion::Tag::nullable_iface,
+  ::android::aidl::tests::ListOfInterfaces::MyUnion::Tag::iface_list,
+  ::android::aidl::tests::ListOfInterfaces::MyUnion::Tag::nullable_iface_list,
+};
+#pragma clang diagnostic pop
+}  // namespace internal
+}  // namespace android
diff --git a/tests/golden_output/aidl-test-interface-cpp-source/gen/include/android/aidl/tests/Union.h b/tests/golden_output/aidl-test-interface-cpp-source/gen/include/android/aidl/tests/Union.h
index 62c1bf4..a82300f 100644
--- a/tests/golden_output/aidl-test-interface-cpp-source/gen/include/android/aidl/tests/Union.h
+++ b/tests/golden_output/aidl-test-interface-cpp-source/gen/include/android/aidl/tests/Union.h
@@ -2,6 +2,8 @@
 
 #include <android/aidl/tests/ByteEnum.h>
 #include <android/binder_to_string.h>
+#include <array>
+#include <binder/Enums.h>
 #include <binder/IBinder.h>
 #include <binder/Parcel.h>
 #include <binder/Status.h>
@@ -23,20 +25,28 @@
 namespace tests {
 class Union : public ::android::Parcelable {
 public:
-  enum Tag : int32_t {
-    ns = 0,  // int[] ns;
-    n,  // int n;
-    m,  // int m;
-    s,  // String s;
-    ibinder,  // IBinder ibinder;
-    ss,  // List<String> ss;
-    be,  // android.aidl.tests.ByteEnum be;
+  enum class Tag : int32_t {
+    ns = 0,
+    n = 1,
+    m = 2,
+    s = 3,
+    ibinder = 4,
+    ss = 5,
+    be = 6,
   };
+  // Expose tag symbols for legacy code
+  static const inline Tag ns = Tag::ns;
+  static const inline Tag n = Tag::n;
+  static const inline Tag m = Tag::m;
+  static const inline Tag s = Tag::s;
+  static const inline Tag ibinder = Tag::ibinder;
+  static const inline Tag ss = Tag::ss;
+  static const inline Tag be = Tag::be;
 
   template<typename _Tp>
   static constexpr bool _not_self = !std::is_same_v<std::remove_cv_t<std::remove_reference_t<_Tp>>, Union>;
 
-  Union() : _value(std::in_place_index<ns>, ::std::vector<int32_t>({})) { }
+  Union() : _value(std::in_place_index<static_cast<size_t>(ns)>, ::std::vector<int32_t>({})) { }
 
   template <typename _Tp, typename = std::enable_if_t<_not_self<_Tp>>>
   // NOLINTNEXTLINE(google-explicit-constructor)
@@ -49,12 +59,12 @@
 
   template <Tag _tag, typename... _Tp>
   static Union make(_Tp&&... _args) {
-    return Union(std::in_place_index<_tag>, std::forward<_Tp>(_args)...);
+    return Union(std::in_place_index<static_cast<size_t>(_tag)>, std::forward<_Tp>(_args)...);
   }
 
   template <Tag _tag, typename _Tp, typename... _Up>
   static Union make(std::initializer_list<_Tp> _il, _Up&&... _args) {
-    return Union(std::in_place_index<_tag>, std::move(_il), std::forward<_Up>(_args)...);
+    return Union(std::in_place_index<static_cast<size_t>(_tag)>, std::move(_il), std::forward<_Up>(_args)...);
   }
 
   Tag getTag() const {
@@ -64,18 +74,18 @@
   template <Tag _tag>
   const auto& get() const {
     if (getTag() != _tag) { __assert2(__FILE__, __LINE__, __PRETTY_FUNCTION__, "bad access: a wrong tag"); }
-    return std::get<_tag>(_value);
+    return std::get<static_cast<size_t>(_tag)>(_value);
   }
 
   template <Tag _tag>
   auto& get() {
     if (getTag() != _tag) { __assert2(__FILE__, __LINE__, __PRETTY_FUNCTION__, "bad access: a wrong tag"); }
-    return std::get<_tag>(_value);
+    return std::get<static_cast<size_t>(_tag)>(_value);
   }
 
   template <Tag _tag, typename... _Tp>
   void set(_Tp&&... _args) {
-    _value.emplace<_tag>(std::forward<_Tp>(_args)...);
+    _value.emplace<static_cast<size_t>(_tag)>(std::forward<_Tp>(_args)...);
   }
 
   inline bool operator!=(const Union& rhs) const {
@@ -125,3 +135,46 @@
 }  // namespace tests
 }  // namespace aidl
 }  // namespace android
+namespace android {
+namespace aidl {
+namespace tests {
+[[nodiscard]] static inline std::string toString(Union::Tag val) {
+  switch(val) {
+  case Union::Tag::ns:
+    return "ns";
+  case Union::Tag::n:
+    return "n";
+  case Union::Tag::m:
+    return "m";
+  case Union::Tag::s:
+    return "s";
+  case Union::Tag::ibinder:
+    return "ibinder";
+  case Union::Tag::ss:
+    return "ss";
+  case Union::Tag::be:
+    return "be";
+  default:
+    return std::to_string(static_cast<int32_t>(val));
+  }
+}
+}  // namespace tests
+}  // namespace aidl
+}  // namespace android
+namespace android {
+namespace internal {
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wc++17-extensions"
+template <>
+constexpr inline std::array<::android::aidl::tests::Union::Tag, 7> enum_values<::android::aidl::tests::Union::Tag> = {
+  ::android::aidl::tests::Union::Tag::ns,
+  ::android::aidl::tests::Union::Tag::n,
+  ::android::aidl::tests::Union::Tag::m,
+  ::android::aidl::tests::Union::Tag::s,
+  ::android::aidl::tests::Union::Tag::ibinder,
+  ::android::aidl::tests::Union::Tag::ss,
+  ::android::aidl::tests::Union::Tag::be,
+};
+#pragma clang diagnostic pop
+}  // namespace internal
+}  // namespace android
diff --git a/tests/golden_output/aidl-test-interface-cpp-source/gen/include/android/aidl/tests/UnionWithFd.h b/tests/golden_output/aidl-test-interface-cpp-source/gen/include/android/aidl/tests/UnionWithFd.h
index 373aba7..c97edf1 100644
--- a/tests/golden_output/aidl-test-interface-cpp-source/gen/include/android/aidl/tests/UnionWithFd.h
+++ b/tests/golden_output/aidl-test-interface-cpp-source/gen/include/android/aidl/tests/UnionWithFd.h
@@ -1,11 +1,14 @@
 #pragma once
 
 #include <android/binder_to_string.h>
+#include <array>
+#include <binder/Enums.h>
 #include <binder/Parcel.h>
 #include <binder/ParcelFileDescriptor.h>
 #include <binder/Status.h>
 #include <cassert>
 #include <cstdint>
+#include <string>
 #include <type_traits>
 #include <utility>
 #include <utils/String16.h>
@@ -20,15 +23,18 @@
 namespace tests {
 class UnionWithFd : public ::android::Parcelable {
 public:
-  enum Tag : int32_t {
-    num = 0,  // int num;
-    pfd,  // ParcelFileDescriptor pfd;
+  enum class Tag : int32_t {
+    num = 0,
+    pfd = 1,
   };
+  // Expose tag symbols for legacy code
+  static const inline Tag num = Tag::num;
+  static const inline Tag pfd = Tag::pfd;
 
   template<typename _Tp>
   static constexpr bool _not_self = !std::is_same_v<std::remove_cv_t<std::remove_reference_t<_Tp>>, UnionWithFd>;
 
-  UnionWithFd() : _value(std::in_place_index<num>, int32_t(0)) { }
+  UnionWithFd() : _value(std::in_place_index<static_cast<size_t>(num)>, int32_t(0)) { }
 
   template <typename _Tp, typename = std::enable_if_t<_not_self<_Tp>>>
   // NOLINTNEXTLINE(google-explicit-constructor)
@@ -41,12 +47,12 @@
 
   template <Tag _tag, typename... _Tp>
   static UnionWithFd make(_Tp&&... _args) {
-    return UnionWithFd(std::in_place_index<_tag>, std::forward<_Tp>(_args)...);
+    return UnionWithFd(std::in_place_index<static_cast<size_t>(_tag)>, std::forward<_Tp>(_args)...);
   }
 
   template <Tag _tag, typename _Tp, typename... _Up>
   static UnionWithFd make(std::initializer_list<_Tp> _il, _Up&&... _args) {
-    return UnionWithFd(std::in_place_index<_tag>, std::move(_il), std::forward<_Up>(_args)...);
+    return UnionWithFd(std::in_place_index<static_cast<size_t>(_tag)>, std::move(_il), std::forward<_Up>(_args)...);
   }
 
   Tag getTag() const {
@@ -56,18 +62,18 @@
   template <Tag _tag>
   const auto& get() const {
     if (getTag() != _tag) { __assert2(__FILE__, __LINE__, __PRETTY_FUNCTION__, "bad access: a wrong tag"); }
-    return std::get<_tag>(_value);
+    return std::get<static_cast<size_t>(_tag)>(_value);
   }
 
   template <Tag _tag>
   auto& get() {
     if (getTag() != _tag) { __assert2(__FILE__, __LINE__, __PRETTY_FUNCTION__, "bad access: a wrong tag"); }
-    return std::get<_tag>(_value);
+    return std::get<static_cast<size_t>(_tag)>(_value);
   }
 
   template <Tag _tag, typename... _Tp>
   void set(_Tp&&... _args) {
-    _value.emplace<_tag>(std::forward<_Tp>(_args)...);
+    _value.emplace<static_cast<size_t>(_tag)>(std::forward<_Tp>(_args)...);
   }
 
   inline bool operator!=(const UnionWithFd& rhs) const {
@@ -111,3 +117,31 @@
 }  // namespace tests
 }  // namespace aidl
 }  // namespace android
+namespace android {
+namespace aidl {
+namespace tests {
+[[nodiscard]] static inline std::string toString(UnionWithFd::Tag val) {
+  switch(val) {
+  case UnionWithFd::Tag::num:
+    return "num";
+  case UnionWithFd::Tag::pfd:
+    return "pfd";
+  default:
+    return std::to_string(static_cast<int32_t>(val));
+  }
+}
+}  // namespace tests
+}  // namespace aidl
+}  // namespace android
+namespace android {
+namespace internal {
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wc++17-extensions"
+template <>
+constexpr inline std::array<::android::aidl::tests::UnionWithFd::Tag, 2> enum_values<::android::aidl::tests::UnionWithFd::Tag> = {
+  ::android::aidl::tests::UnionWithFd::Tag::num,
+  ::android::aidl::tests::UnionWithFd::Tag::pfd,
+};
+#pragma clang diagnostic pop
+}  // namespace internal
+}  // namespace android
diff --git a/tests/golden_output/aidl-test-interface-cpp-source/gen/include/android/aidl/tests/permission/BnProtected.h b/tests/golden_output/aidl-test-interface-cpp-source/gen/include/android/aidl/tests/permission/BnProtected.h
deleted file mode 100644
index 82340aa..0000000
--- a/tests/golden_output/aidl-test-interface-cpp-source/gen/include/android/aidl/tests/permission/BnProtected.h
+++ /dev/null
@@ -1,38 +0,0 @@
-#pragma once
-
-#include <binder/IInterface.h>
-#include <android/aidl/tests/permission/IProtected.h>
-
-namespace android {
-namespace aidl {
-namespace tests {
-namespace permission {
-class BnProtected : public ::android::BnInterface<IProtected> {
-public:
-  static constexpr uint32_t TRANSACTION_PermissionProtected = ::android::IBinder::FIRST_CALL_TRANSACTION + 0;
-  static constexpr uint32_t TRANSACTION_MultiplePermissionsAll = ::android::IBinder::FIRST_CALL_TRANSACTION + 1;
-  static constexpr uint32_t TRANSACTION_MultiplePermissionsAny = ::android::IBinder::FIRST_CALL_TRANSACTION + 2;
-  explicit BnProtected();
-  ::android::status_t onTransact(uint32_t _aidl_code, const ::android::Parcel& _aidl_data, ::android::Parcel* _aidl_reply, uint32_t _aidl_flags) override;
-};  // class BnProtected
-
-class IProtectedDelegator : public BnProtected {
-public:
-  explicit IProtectedDelegator(::android::sp<IProtected> &impl) : _aidl_delegate(impl) {}
-
-  ::android::binder::Status PermissionProtected() override {
-    return _aidl_delegate->PermissionProtected();
-  }
-  ::android::binder::Status MultiplePermissionsAll() override {
-    return _aidl_delegate->MultiplePermissionsAll();
-  }
-  ::android::binder::Status MultiplePermissionsAny() override {
-    return _aidl_delegate->MultiplePermissionsAny();
-  }
-private:
-  ::android::sp<IProtected> _aidl_delegate;
-};  // class IProtectedDelegator
-}  // namespace permission
-}  // namespace tests
-}  // namespace aidl
-}  // namespace android
diff --git a/tests/golden_output/aidl-test-interface-cpp-source/gen/include/android/aidl/tests/permission/BnProtectedInterface.h b/tests/golden_output/aidl-test-interface-cpp-source/gen/include/android/aidl/tests/permission/BnProtectedInterface.h
deleted file mode 100644
index 7516a17..0000000
--- a/tests/golden_output/aidl-test-interface-cpp-source/gen/include/android/aidl/tests/permission/BnProtectedInterface.h
+++ /dev/null
@@ -1,34 +0,0 @@
-#pragma once
-
-#include <binder/IInterface.h>
-#include <android/aidl/tests/permission/IProtectedInterface.h>
-
-namespace android {
-namespace aidl {
-namespace tests {
-namespace permission {
-class BnProtectedInterface : public ::android::BnInterface<IProtectedInterface> {
-public:
-  static constexpr uint32_t TRANSACTION_Method1 = ::android::IBinder::FIRST_CALL_TRANSACTION + 0;
-  static constexpr uint32_t TRANSACTION_Method2 = ::android::IBinder::FIRST_CALL_TRANSACTION + 1;
-  explicit BnProtectedInterface();
-  ::android::status_t onTransact(uint32_t _aidl_code, const ::android::Parcel& _aidl_data, ::android::Parcel* _aidl_reply, uint32_t _aidl_flags) override;
-};  // class BnProtectedInterface
-
-class IProtectedInterfaceDelegator : public BnProtectedInterface {
-public:
-  explicit IProtectedInterfaceDelegator(::android::sp<IProtectedInterface> &impl) : _aidl_delegate(impl) {}
-
-  ::android::binder::Status Method1() override {
-    return _aidl_delegate->Method1();
-  }
-  ::android::binder::Status Method2() override {
-    return _aidl_delegate->Method2();
-  }
-private:
-  ::android::sp<IProtectedInterface> _aidl_delegate;
-};  // class IProtectedInterfaceDelegator
-}  // namespace permission
-}  // namespace tests
-}  // namespace aidl
-}  // namespace android
diff --git a/tests/golden_output/aidl-test-interface-cpp-source/gen/include/android/aidl/tests/permission/BpProtected.h b/tests/golden_output/aidl-test-interface-cpp-source/gen/include/android/aidl/tests/permission/BpProtected.h
deleted file mode 100644
index fa83ef0..0000000
--- a/tests/golden_output/aidl-test-interface-cpp-source/gen/include/android/aidl/tests/permission/BpProtected.h
+++ /dev/null
@@ -1,23 +0,0 @@
-#pragma once
-
-#include <binder/IBinder.h>
-#include <binder/IInterface.h>
-#include <utils/Errors.h>
-#include <android/aidl/tests/permission/IProtected.h>
-
-namespace android {
-namespace aidl {
-namespace tests {
-namespace permission {
-class BpProtected : public ::android::BpInterface<IProtected> {
-public:
-  explicit BpProtected(const ::android::sp<::android::IBinder>& _aidl_impl);
-  virtual ~BpProtected() = default;
-  ::android::binder::Status PermissionProtected() override;
-  ::android::binder::Status MultiplePermissionsAll() override;
-  ::android::binder::Status MultiplePermissionsAny() override;
-};  // class BpProtected
-}  // namespace permission
-}  // namespace tests
-}  // namespace aidl
-}  // namespace android
diff --git a/tests/golden_output/aidl-test-interface-cpp-source/gen/include/android/aidl/tests/permission/BpProtectedInterface.h b/tests/golden_output/aidl-test-interface-cpp-source/gen/include/android/aidl/tests/permission/BpProtectedInterface.h
deleted file mode 100644
index c77b59a..0000000
--- a/tests/golden_output/aidl-test-interface-cpp-source/gen/include/android/aidl/tests/permission/BpProtectedInterface.h
+++ /dev/null
@@ -1,22 +0,0 @@
-#pragma once
-
-#include <binder/IBinder.h>
-#include <binder/IInterface.h>
-#include <utils/Errors.h>
-#include <android/aidl/tests/permission/IProtectedInterface.h>
-
-namespace android {
-namespace aidl {
-namespace tests {
-namespace permission {
-class BpProtectedInterface : public ::android::BpInterface<IProtectedInterface> {
-public:
-  explicit BpProtectedInterface(const ::android::sp<::android::IBinder>& _aidl_impl);
-  virtual ~BpProtectedInterface() = default;
-  ::android::binder::Status Method1() override;
-  ::android::binder::Status Method2() override;
-};  // class BpProtectedInterface
-}  // namespace permission
-}  // namespace tests
-}  // namespace aidl
-}  // namespace android
diff --git a/tests/golden_output/aidl-test-interface-cpp-source/gen/include/android/aidl/tests/permission/IProtected.h b/tests/golden_output/aidl-test-interface-cpp-source/gen/include/android/aidl/tests/permission/IProtected.h
deleted file mode 100644
index d8b05b9..0000000
--- a/tests/golden_output/aidl-test-interface-cpp-source/gen/include/android/aidl/tests/permission/IProtected.h
+++ /dev/null
@@ -1,38 +0,0 @@
-#pragma once
-
-#include <binder/IBinder.h>
-#include <binder/IInterface.h>
-#include <binder/Status.h>
-#include <utils/StrongPointer.h>
-
-namespace android {
-namespace aidl {
-namespace tests {
-namespace permission {
-class IProtected : public ::android::IInterface {
-public:
-  DECLARE_META_INTERFACE(Protected)
-  virtual ::android::binder::Status PermissionProtected() = 0;
-  virtual ::android::binder::Status MultiplePermissionsAll() = 0;
-  virtual ::android::binder::Status MultiplePermissionsAny() = 0;
-};  // class IProtected
-
-class IProtectedDefault : public IProtected {
-public:
-  ::android::IBinder* onAsBinder() override {
-    return nullptr;
-  }
-  ::android::binder::Status PermissionProtected() override {
-    return ::android::binder::Status::fromStatusT(::android::UNKNOWN_TRANSACTION);
-  }
-  ::android::binder::Status MultiplePermissionsAll() override {
-    return ::android::binder::Status::fromStatusT(::android::UNKNOWN_TRANSACTION);
-  }
-  ::android::binder::Status MultiplePermissionsAny() override {
-    return ::android::binder::Status::fromStatusT(::android::UNKNOWN_TRANSACTION);
-  }
-};  // class IProtectedDefault
-}  // namespace permission
-}  // namespace tests
-}  // namespace aidl
-}  // namespace android
diff --git a/tests/golden_output/aidl-test-interface-cpp-source/gen/include/android/aidl/tests/permission/IProtectedInterface.h b/tests/golden_output/aidl-test-interface-cpp-source/gen/include/android/aidl/tests/permission/IProtectedInterface.h
deleted file mode 100644
index 82aaceb..0000000
--- a/tests/golden_output/aidl-test-interface-cpp-source/gen/include/android/aidl/tests/permission/IProtectedInterface.h
+++ /dev/null
@@ -1,34 +0,0 @@
-#pragma once
-
-#include <binder/IBinder.h>
-#include <binder/IInterface.h>
-#include <binder/Status.h>
-#include <utils/StrongPointer.h>
-
-namespace android {
-namespace aidl {
-namespace tests {
-namespace permission {
-class IProtectedInterface : public ::android::IInterface {
-public:
-  DECLARE_META_INTERFACE(ProtectedInterface)
-  virtual ::android::binder::Status Method1() = 0;
-  virtual ::android::binder::Status Method2() = 0;
-};  // class IProtectedInterface
-
-class IProtectedInterfaceDefault : public IProtectedInterface {
-public:
-  ::android::IBinder* onAsBinder() override {
-    return nullptr;
-  }
-  ::android::binder::Status Method1() override {
-    return ::android::binder::Status::fromStatusT(::android::UNKNOWN_TRANSACTION);
-  }
-  ::android::binder::Status Method2() override {
-    return ::android::binder::Status::fromStatusT(::android::UNKNOWN_TRANSACTION);
-  }
-};  // class IProtectedInterfaceDefault
-}  // namespace permission
-}  // namespace tests
-}  // namespace aidl
-}  // namespace android
diff --git a/tests/golden_output/aidl-test-interface-cpp-source/gen/include/android/aidl/tests/unions/EnumUnion.h b/tests/golden_output/aidl-test-interface-cpp-source/gen/include/android/aidl/tests/unions/EnumUnion.h
index 348e34d..7a41b20 100644
--- a/tests/golden_output/aidl-test-interface-cpp-source/gen/include/android/aidl/tests/unions/EnumUnion.h
+++ b/tests/golden_output/aidl-test-interface-cpp-source/gen/include/android/aidl/tests/unions/EnumUnion.h
@@ -3,9 +3,13 @@
 #include <android/aidl/tests/IntEnum.h>
 #include <android/aidl/tests/LongEnum.h>
 #include <android/binder_to_string.h>
+#include <array>
+#include <binder/Enums.h>
 #include <binder/Parcel.h>
 #include <binder/Status.h>
 #include <cassert>
+#include <cstdint>
+#include <string>
 #include <type_traits>
 #include <utility>
 #include <utils/String16.h>
@@ -21,15 +25,20 @@
 namespace unions {
 class EnumUnion : public ::android::Parcelable {
 public:
-  enum Tag : int32_t {
-    intEnum = 0,  // android.aidl.tests.IntEnum intEnum;
-    longEnum,  // android.aidl.tests.LongEnum longEnum;
+  enum class Tag : int32_t {
+    intEnum = 0,
+    longEnum = 1,
+    deprecatedField __attribute__((deprecated("do not use this"))) = 2,
   };
+  // Expose tag symbols for legacy code
+  static const inline Tag intEnum = Tag::intEnum;
+  static const inline Tag longEnum = Tag::longEnum;
+  static const inline Tag __attribute__((deprecated("do not use this"))) deprecatedField = Tag::deprecatedField;
 
   template<typename _Tp>
   static constexpr bool _not_self = !std::is_same_v<std::remove_cv_t<std::remove_reference_t<_Tp>>, EnumUnion>;
 
-  EnumUnion() : _value(std::in_place_index<intEnum>, ::android::aidl::tests::IntEnum(::android::aidl::tests::IntEnum::FOO)) { }
+  EnumUnion() : _value(std::in_place_index<static_cast<size_t>(intEnum)>, ::android::aidl::tests::IntEnum(::android::aidl::tests::IntEnum::FOO)) { }
 
   template <typename _Tp, typename = std::enable_if_t<_not_self<_Tp>>>
   // NOLINTNEXTLINE(google-explicit-constructor)
@@ -42,12 +51,12 @@
 
   template <Tag _tag, typename... _Tp>
   static EnumUnion make(_Tp&&... _args) {
-    return EnumUnion(std::in_place_index<_tag>, std::forward<_Tp>(_args)...);
+    return EnumUnion(std::in_place_index<static_cast<size_t>(_tag)>, std::forward<_Tp>(_args)...);
   }
 
   template <Tag _tag, typename _Tp, typename... _Up>
   static EnumUnion make(std::initializer_list<_Tp> _il, _Up&&... _args) {
-    return EnumUnion(std::in_place_index<_tag>, std::move(_il), std::forward<_Up>(_args)...);
+    return EnumUnion(std::in_place_index<static_cast<size_t>(_tag)>, std::move(_il), std::forward<_Up>(_args)...);
   }
 
   Tag getTag() const {
@@ -57,18 +66,18 @@
   template <Tag _tag>
   const auto& get() const {
     if (getTag() != _tag) { __assert2(__FILE__, __LINE__, __PRETTY_FUNCTION__, "bad access: a wrong tag"); }
-    return std::get<_tag>(_value);
+    return std::get<static_cast<size_t>(_tag)>(_value);
   }
 
   template <Tag _tag>
   auto& get() {
     if (getTag() != _tag) { __assert2(__FILE__, __LINE__, __PRETTY_FUNCTION__, "bad access: a wrong tag"); }
-    return std::get<_tag>(_value);
+    return std::get<static_cast<size_t>(_tag)>(_value);
   }
 
   template <Tag _tag, typename... _Tp>
   void set(_Tp&&... _args) {
-    _value.emplace<_tag>(std::forward<_Tp>(_args)...);
+    _value.emplace<static_cast<size_t>(_tag)>(std::forward<_Tp>(_args)...);
   }
 
   inline bool operator!=(const EnumUnion& rhs) const {
@@ -102,14 +111,55 @@
     switch (getTag()) {
     case intEnum: os << "intEnum: " << ::android::internal::ToString(get<intEnum>()); break;
     case longEnum: os << "longEnum: " << ::android::internal::ToString(get<longEnum>()); break;
+    #pragma clang diagnostic push
+    #pragma clang diagnostic ignored "-Wdeprecated-declarations"
+    case deprecatedField: os << "deprecatedField: " << ::android::internal::ToString(get<deprecatedField>()); break;
+    #pragma clang diagnostic pop
     }
     os << "}";
     return os.str();
   }
 private:
-  std::variant<::android::aidl::tests::IntEnum, ::android::aidl::tests::LongEnum> _value;
+  std::variant<::android::aidl::tests::IntEnum, ::android::aidl::tests::LongEnum, int32_t> _value;
 };  // class EnumUnion
 }  // namespace unions
 }  // namespace tests
 }  // namespace aidl
 }  // namespace android
+namespace android {
+namespace aidl {
+namespace tests {
+namespace unions {
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wdeprecated-declarations"
+[[nodiscard]] static inline std::string toString(EnumUnion::Tag val) {
+  switch(val) {
+  case EnumUnion::Tag::intEnum:
+    return "intEnum";
+  case EnumUnion::Tag::longEnum:
+    return "longEnum";
+  case EnumUnion::Tag::deprecatedField:
+    return "deprecatedField";
+  default:
+    return std::to_string(static_cast<int32_t>(val));
+  }
+}
+#pragma clang diagnostic pop
+}  // namespace unions
+}  // namespace tests
+}  // namespace aidl
+}  // namespace android
+namespace android {
+namespace internal {
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wc++17-extensions"
+#pragma clang diagnostic ignored "-Wdeprecated-declarations"
+template <>
+constexpr inline std::array<::android::aidl::tests::unions::EnumUnion::Tag, 3> enum_values<::android::aidl::tests::unions::EnumUnion::Tag> = {
+  ::android::aidl::tests::unions::EnumUnion::Tag::intEnum,
+  ::android::aidl::tests::unions::EnumUnion::Tag::longEnum,
+  ::android::aidl::tests::unions::EnumUnion::Tag::deprecatedField,
+};
+#pragma clang diagnostic pop
+}  // namespace internal
+}  // namespace android
diff --git a/tests/golden_output/aidl-test-interface-cpp-source/gen/include/android/aidl/tests/unions/UnionInUnion.h b/tests/golden_output/aidl-test-interface-cpp-source/gen/include/android/aidl/tests/unions/UnionInUnion.h
index d83935c..9badc3a 100644
--- a/tests/golden_output/aidl-test-interface-cpp-source/gen/include/android/aidl/tests/unions/UnionInUnion.h
+++ b/tests/golden_output/aidl-test-interface-cpp-source/gen/include/android/aidl/tests/unions/UnionInUnion.h
@@ -2,10 +2,13 @@
 
 #include <android/aidl/tests/unions/EnumUnion.h>
 #include <android/binder_to_string.h>
+#include <array>
+#include <binder/Enums.h>
 #include <binder/Parcel.h>
 #include <binder/Status.h>
 #include <cassert>
 #include <cstdint>
+#include <string>
 #include <type_traits>
 #include <utility>
 #include <utils/String16.h>
@@ -21,15 +24,18 @@
 namespace unions {
 class UnionInUnion : public ::android::Parcelable {
 public:
-  enum Tag : int32_t {
-    first = 0,  // android.aidl.tests.unions.EnumUnion first;
-    second,  // int second;
+  enum class Tag : int32_t {
+    first = 0,
+    second = 1,
   };
+  // Expose tag symbols for legacy code
+  static const inline Tag first = Tag::first;
+  static const inline Tag second = Tag::second;
 
   template<typename _Tp>
   static constexpr bool _not_self = !std::is_same_v<std::remove_cv_t<std::remove_reference_t<_Tp>>, UnionInUnion>;
 
-  UnionInUnion() : _value(std::in_place_index<first>, ::android::aidl::tests::unions::EnumUnion()) { }
+  UnionInUnion() : _value(std::in_place_index<static_cast<size_t>(first)>, ::android::aidl::tests::unions::EnumUnion()) { }
 
   template <typename _Tp, typename = std::enable_if_t<_not_self<_Tp>>>
   // NOLINTNEXTLINE(google-explicit-constructor)
@@ -42,12 +48,12 @@
 
   template <Tag _tag, typename... _Tp>
   static UnionInUnion make(_Tp&&... _args) {
-    return UnionInUnion(std::in_place_index<_tag>, std::forward<_Tp>(_args)...);
+    return UnionInUnion(std::in_place_index<static_cast<size_t>(_tag)>, std::forward<_Tp>(_args)...);
   }
 
   template <Tag _tag, typename _Tp, typename... _Up>
   static UnionInUnion make(std::initializer_list<_Tp> _il, _Up&&... _args) {
-    return UnionInUnion(std::in_place_index<_tag>, std::move(_il), std::forward<_Up>(_args)...);
+    return UnionInUnion(std::in_place_index<static_cast<size_t>(_tag)>, std::move(_il), std::forward<_Up>(_args)...);
   }
 
   Tag getTag() const {
@@ -57,18 +63,18 @@
   template <Tag _tag>
   const auto& get() const {
     if (getTag() != _tag) { __assert2(__FILE__, __LINE__, __PRETTY_FUNCTION__, "bad access: a wrong tag"); }
-    return std::get<_tag>(_value);
+    return std::get<static_cast<size_t>(_tag)>(_value);
   }
 
   template <Tag _tag>
   auto& get() {
     if (getTag() != _tag) { __assert2(__FILE__, __LINE__, __PRETTY_FUNCTION__, "bad access: a wrong tag"); }
-    return std::get<_tag>(_value);
+    return std::get<static_cast<size_t>(_tag)>(_value);
   }
 
   template <Tag _tag, typename... _Tp>
   void set(_Tp&&... _args) {
-    _value.emplace<_tag>(std::forward<_Tp>(_args)...);
+    _value.emplace<static_cast<size_t>(_tag)>(std::forward<_Tp>(_args)...);
   }
 
   inline bool operator!=(const UnionInUnion& rhs) const {
@@ -113,3 +119,33 @@
 }  // namespace tests
 }  // namespace aidl
 }  // namespace android
+namespace android {
+namespace aidl {
+namespace tests {
+namespace unions {
+[[nodiscard]] static inline std::string toString(UnionInUnion::Tag val) {
+  switch(val) {
+  case UnionInUnion::Tag::first:
+    return "first";
+  case UnionInUnion::Tag::second:
+    return "second";
+  default:
+    return std::to_string(static_cast<int32_t>(val));
+  }
+}
+}  // namespace unions
+}  // namespace tests
+}  // namespace aidl
+}  // namespace android
+namespace android {
+namespace internal {
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wc++17-extensions"
+template <>
+constexpr inline std::array<::android::aidl::tests::unions::UnionInUnion::Tag, 2> enum_values<::android::aidl::tests::unions::UnionInUnion::Tag> = {
+  ::android::aidl::tests::unions::UnionInUnion::Tag::first,
+  ::android::aidl::tests::unions::UnionInUnion::Tag::second,
+};
+#pragma clang diagnostic pop
+}  // namespace internal
+}  // namespace android
diff --git a/tests/golden_output/aidl-test-interface-java-source/gen/android/aidl/tests/ArrayOfInterfaces.java b/tests/golden_output/aidl-test-interface-java-source/gen/android/aidl/tests/ArrayOfInterfaces.java
index 5c86379..ea45e08 100644
--- a/tests/golden_output/aidl-test-interface-java-source/gen/android/aidl/tests/ArrayOfInterfaces.java
+++ b/tests/golden_output/aidl-test-interface-java-source/gen/android/aidl/tests/ArrayOfInterfaces.java
@@ -565,5 +565,11 @@
       this._tag = _tag;
       this._value = _value;
     }
+    public static @interface Tag {
+      public static final int iface = 0;
+      public static final int nullable_iface = 1;
+      public static final int iface_array = 2;
+      public static final int nullable_iface_array = 3;
+    }
   }
 }
diff --git a/tests/golden_output/aidl-test-interface-java-source/gen/android/aidl/tests/FixedSize.java b/tests/golden_output/aidl-test-interface-java-source/gen/android/aidl/tests/FixedSize.java
index 1b4ca32..4837063 100644
--- a/tests/golden_output/aidl-test-interface-java-source/gen/android/aidl/tests/FixedSize.java
+++ b/tests/golden_output/aidl-test-interface-java-source/gen/android/aidl/tests/FixedSize.java
@@ -78,7 +78,7 @@
       _aidl_parcel.writeFloat(floatValue);
       _aidl_parcel.writeDouble(doubleValue);
       _aidl_parcel.writeLong(enumValue);
-      _aidl_parcel.writeTypedObject(parcelableValue, 0);
+      _aidl_parcel.writeTypedObject(parcelableValue, _aidl_flag);
       int _aidl_end_pos = _aidl_parcel.dataPosition();
       _aidl_parcel.setDataPosition(_aidl_start_pos);
       _aidl_parcel.writeInt(_aidl_end_pos - _aidl_start_pos);
@@ -404,5 +404,15 @@
       this._tag = _tag;
       this._value = _value;
     }
+    public static @interface Tag {
+      public static final byte booleanValue = 0;
+      public static final byte byteValue = 1;
+      public static final byte charValue = 2;
+      public static final byte intValue = 3;
+      public static final byte longValue = 4;
+      public static final byte floatValue = 5;
+      public static final byte doubleValue = 6;
+      public static final byte enumValue = 7;
+    }
   }
 }
diff --git a/tests/golden_output/aidl-test-interface-java-source/gen/android/aidl/tests/ITestService.java b/tests/golden_output/aidl-test-interface-java-source/gen/android/aidl/tests/ITestService.java
index 50d65a6..c263576 100644
--- a/tests/golden_output/aidl-test-interface-java-source/gen/android/aidl/tests/ITestService.java
+++ b/tests/golden_output/aidl-test-interface-java-source/gen/android/aidl/tests/ITestService.java
@@ -276,6 +276,10 @@
     {
       return null;
     }
+    @Override public int[] GetUnionTags(android.aidl.tests.Union[] input) throws android.os.RemoteException
+    {
+      return null;
+    }
     // Retrieve the ICppJavaTests if the server supports it
     @Override public android.os.IBinder GetCppJavaTests() throws android.os.RemoteException
     {
@@ -573,6 +577,10 @@
     {
       return mImpl.GetNewNameInterface();
     }
+    @Override public int[] GetUnionTags(android.aidl.tests.Union[] input) throws android.os.RemoteException
+    {
+      return mImpl.GetUnionTags(input);
+    }
     // Retrieve the ICppJavaTests if the server supports it
     @Override public android.os.IBinder GetCppJavaTests() throws android.os.RemoteException
     {
@@ -1381,6 +1389,16 @@
           reply.writeStrongInterface(_result);
           break;
         }
+        case TRANSACTION_GetUnionTags:
+        {
+          android.aidl.tests.Union[] _arg0;
+          _arg0 = data.createTypedArray(android.aidl.tests.Union.CREATOR);
+          data.enforceNoDataAvail();
+          int[] _result = this.GetUnionTags(_arg0);
+          reply.writeNoException();
+          reply.writeIntArray(_result);
+          break;
+        }
         case TRANSACTION_GetCppJavaTests:
         {
           android.os.IBinder _result = this.GetCppJavaTests();
@@ -3054,6 +3072,30 @@
         }
         return _result;
       }
+      @Override public int[] GetUnionTags(android.aidl.tests.Union[] input) throws android.os.RemoteException
+      {
+        android.os.Parcel _data = android.os.Parcel.obtain(asBinder());
+        _data.markSensitive();
+        android.os.Parcel _reply = android.os.Parcel.obtain();
+        int[] _result;
+        try {
+          _data.writeInterfaceToken(DESCRIPTOR);
+          _data.writeTypedArray(input, 0);
+          boolean _status = mRemote.transact(Stub.TRANSACTION_GetUnionTags, _data, _reply, android.os.IBinder.FLAG_CLEAR_BUF);
+          if (!_status) {
+            if (getDefaultImpl() != null) {
+              return getDefaultImpl().GetUnionTags(input);
+            }
+          }
+          _reply.readException();
+          _result = _reply.createIntArray();
+        }
+        finally {
+          _reply.recycle();
+          _data.recycle();
+        }
+        return _result;
+      }
       // Retrieve the ICppJavaTests if the server supports it
       @Override public android.os.IBinder GetCppJavaTests() throws android.os.RemoteException
       {
@@ -3166,8 +3208,9 @@
     static final int TRANSACTION_ReverseNullableIBinderArray = (android.os.IBinder.FIRST_CALL_TRANSACTION + 60);
     static final int TRANSACTION_GetOldNameInterface = (android.os.IBinder.FIRST_CALL_TRANSACTION + 61);
     static final int TRANSACTION_GetNewNameInterface = (android.os.IBinder.FIRST_CALL_TRANSACTION + 62);
-    static final int TRANSACTION_GetCppJavaTests = (android.os.IBinder.FIRST_CALL_TRANSACTION + 63);
-    static final int TRANSACTION_getBackendType = (android.os.IBinder.FIRST_CALL_TRANSACTION + 64);
+    static final int TRANSACTION_GetUnionTags = (android.os.IBinder.FIRST_CALL_TRANSACTION + 63);
+    static final int TRANSACTION_GetCppJavaTests = (android.os.IBinder.FIRST_CALL_TRANSACTION + 64);
+    static final int TRANSACTION_getBackendType = (android.os.IBinder.FIRST_CALL_TRANSACTION + 65);
     public static boolean setDefaultImpl(android.aidl.tests.ITestService impl) {
       // Only one user of this interface can use this function
       // at a time. This is a heuristic to detect if two different
@@ -3357,6 +3400,7 @@
   public android.os.IBinder[] ReverseNullableIBinderArray(android.os.IBinder[] input, android.os.IBinder[] repeated) throws android.os.RemoteException;
   public android.aidl.tests.IOldName GetOldNameInterface() throws android.os.RemoteException;
   public android.aidl.tests.INewName GetNewNameInterface() throws android.os.RemoteException;
+  public int[] GetUnionTags(android.aidl.tests.Union[] input) throws android.os.RemoteException;
   // Retrieve the ICppJavaTests if the server supports it
   public android.os.IBinder GetCppJavaTests() throws android.os.RemoteException;
   public byte getBackendType() throws android.os.RemoteException;
@@ -3461,16 +3505,16 @@
       _aidl_parcel.writeBinderArray(nullable_binder_array);
       _aidl_parcel.writeBinderList(binder_list);
       _aidl_parcel.writeBinderList(nullable_binder_list);
-      _aidl_parcel.writeTypedObject(pfd, 0);
-      _aidl_parcel.writeTypedObject(nullable_pfd, 0);
-      _aidl_parcel.writeTypedArray(pfd_array, 0);
-      _aidl_parcel.writeTypedArray(nullable_pfd_array, 0);
+      _aidl_parcel.writeTypedObject(pfd, _aidl_flag);
+      _aidl_parcel.writeTypedObject(nullable_pfd, _aidl_flag);
+      _aidl_parcel.writeTypedArray(pfd_array, _aidl_flag);
+      _aidl_parcel.writeTypedArray(nullable_pfd_array, _aidl_flag);
       _aidl_parcel.writeTypedList(pfd_list);
       _aidl_parcel.writeTypedList(nullable_pfd_list);
-      _aidl_parcel.writeTypedObject(parcel, 0);
-      _aidl_parcel.writeTypedObject(nullable_parcel, 0);
-      _aidl_parcel.writeTypedArray(parcel_array, 0);
-      _aidl_parcel.writeTypedArray(nullable_parcel_array, 0);
+      _aidl_parcel.writeTypedObject(parcel, _aidl_flag);
+      _aidl_parcel.writeTypedObject(nullable_parcel, _aidl_flag);
+      _aidl_parcel.writeTypedArray(parcel_array, _aidl_flag);
+      _aidl_parcel.writeTypedArray(nullable_parcel_array, _aidl_flag);
       _aidl_parcel.writeTypedList(parcel_list);
       _aidl_parcel.writeTypedList(nullable_parcel_list);
       int _aidl_end_pos = _aidl_parcel.dataPosition();
diff --git a/tests/golden_output/aidl-test-interface-java-source/gen/android/aidl/tests/ITestService.java.d b/tests/golden_output/aidl-test-interface-java-source/gen/android/aidl/tests/ITestService.java.d
index f82b264..561c508 100644
--- a/tests/golden_output/aidl-test-interface-java-source/gen/android/aidl/tests/ITestService.java.d
+++ b/tests/golden_output/aidl-test-interface-java-source/gen/android/aidl/tests/ITestService.java.d
@@ -9,6 +9,6 @@
   system/tools/aidl/tests/android/aidl/tests/LongEnum.aidl \
   system/tools/aidl/tests/android/aidl/tests/RecursiveList.aidl \
   system/tools/aidl/tests/android/aidl/tests/StructuredParcelable.aidl \
+  system/tools/aidl/tests/android/aidl/tests/Union.aidl \
   system/tools/aidl/tests/android/aidl/tests/extension/ExtendableParcelable.aidl \
-  system/tools/aidl/tests/android/aidl/tests/ConstantExpressionEnum.aidl \
-  system/tools/aidl/tests/android/aidl/tests/Union.aidl
+  system/tools/aidl/tests/android/aidl/tests/ConstantExpressionEnum.aidl
diff --git a/tests/golden_output/aidl-test-interface-java-source/gen/android/aidl/tests/IntEnum.java b/tests/golden_output/aidl-test-interface-java-source/gen/android/aidl/tests/IntEnum.java
index 64ae00a..4114fe7 100644
--- a/tests/golden_output/aidl-test-interface-java-source/gen/android/aidl/tests/IntEnum.java
+++ b/tests/golden_output/aidl-test-interface-java-source/gen/android/aidl/tests/IntEnum.java
@@ -6,4 +6,34 @@
   public static final int FOO = 1000;
   public static final int BAR = 2000;
   public static final int BAZ = 2001;
+  /** @deprecated do not use this */
+  @Deprecated
+  public static final int QUX = 2002;
+  interface $ {
+    static String toString(int _aidl_v) {
+      if (_aidl_v == FOO) return "FOO";
+      if (_aidl_v == BAR) return "BAR";
+      if (_aidl_v == BAZ) return "BAZ";
+      if (_aidl_v == QUX) return "QUX";
+      return Integer.toString(_aidl_v);
+    }
+    static String arrayToString(Object _aidl_v) {
+      if (_aidl_v == null) return "null";
+      Class<?> _aidl_cls = _aidl_v.getClass();
+      if (!_aidl_cls.isArray()) throw new IllegalArgumentException("not an array: " + _aidl_v);
+      Class<?> comp = _aidl_cls.getComponentType();
+      java.util.StringJoiner _aidl_sj = new java.util.StringJoiner(", ", "[", "]");
+      if (comp.isArray()) {
+        for (int _aidl_i = 0; _aidl_i < java.lang.reflect.Array.getLength(_aidl_v); _aidl_i++) {
+          _aidl_sj.add(arrayToString(java.lang.reflect.Array.get(_aidl_v, _aidl_i)));
+        }
+      } else {
+        if (_aidl_cls != int[].class) throw new IllegalArgumentException("wrong type: " + _aidl_cls);
+        for (int e : (int[]) _aidl_v) {
+          _aidl_sj.add(toString(e));
+        }
+      }
+      return _aidl_sj.toString();
+    }
+  }
 }
diff --git a/tests/golden_output/aidl-test-interface-java-source/gen/android/aidl/tests/ListOfInterfaces.java b/tests/golden_output/aidl-test-interface-java-source/gen/android/aidl/tests/ListOfInterfaces.java
index fafba0c..6a2564d 100644
--- a/tests/golden_output/aidl-test-interface-java-source/gen/android/aidl/tests/ListOfInterfaces.java
+++ b/tests/golden_output/aidl-test-interface-java-source/gen/android/aidl/tests/ListOfInterfaces.java
@@ -545,5 +545,11 @@
       this._tag = _tag;
       this._value = _value;
     }
+    public static @interface Tag {
+      public static final int iface = 0;
+      public static final int nullable_iface = 1;
+      public static final int iface_list = 2;
+      public static final int nullable_iface_list = 3;
+    }
   }
 }
diff --git a/tests/golden_output/aidl-test-interface-java-source/gen/android/aidl/tests/ParcelableForToString.java b/tests/golden_output/aidl-test-interface-java-source/gen/android/aidl/tests/ParcelableForToString.java
index c588be1..7032dc7 100644
--- a/tests/golden_output/aidl-test-interface-java-source/gen/android/aidl/tests/ParcelableForToString.java
+++ b/tests/golden_output/aidl-test-interface-java-source/gen/android/aidl/tests/ParcelableForToString.java
@@ -58,14 +58,14 @@
     _aidl_parcel.writeString(stringValue);
     _aidl_parcel.writeStringArray(stringArray);
     _aidl_parcel.writeStringList(stringList);
-    _aidl_parcel.writeTypedObject(parcelableValue, 0);
-    _aidl_parcel.writeTypedArray(parcelableArray, 0);
+    _aidl_parcel.writeTypedObject(parcelableValue, _aidl_flag);
+    _aidl_parcel.writeTypedArray(parcelableArray, _aidl_flag);
     _aidl_parcel.writeInt(enumValue);
     _aidl_parcel.writeIntArray(enumArray);
     _aidl_parcel.writeStringArray(nullArray);
     _aidl_parcel.writeStringList(nullList);
-    _aidl_parcel.writeTypedObject(parcelableGeneric, 0);
-    _aidl_parcel.writeTypedObject(unionValue, 0);
+    _aidl_parcel.writeTypedObject(parcelableGeneric, _aidl_flag);
+    _aidl_parcel.writeTypedObject(unionValue, _aidl_flag);
     int _aidl_end_pos = _aidl_parcel.dataPosition();
     _aidl_parcel.setDataPosition(_aidl_start_pos);
     _aidl_parcel.writeInt(_aidl_end_pos - _aidl_start_pos);
@@ -150,8 +150,8 @@
     _aidl_sj.add("stringList: " + (java.util.Objects.toString(stringList)));
     _aidl_sj.add("parcelableValue: " + (java.util.Objects.toString(parcelableValue)));
     _aidl_sj.add("parcelableArray: " + (java.util.Arrays.toString(parcelableArray)));
-    _aidl_sj.add("enumValue: " + (enumValue));
-    _aidl_sj.add("enumArray: " + (java.util.Arrays.toString(enumArray)));
+    _aidl_sj.add("enumValue: " + (android.aidl.tests.IntEnum.$.toString(enumValue)));
+    _aidl_sj.add("enumArray: " + (android.aidl.tests.IntEnum.$.arrayToString(enumArray)));
     _aidl_sj.add("nullArray: " + (java.util.Arrays.toString(nullArray)));
     _aidl_sj.add("nullList: " + (java.util.Objects.toString(nullList)));
     _aidl_sj.add("parcelableGeneric: " + (java.util.Objects.toString(parcelableGeneric)));
diff --git a/tests/golden_output/aidl-test-interface-java-source/gen/android/aidl/tests/RecursiveList.java b/tests/golden_output/aidl-test-interface-java-source/gen/android/aidl/tests/RecursiveList.java
index a0c6bb6..5efaac9 100644
--- a/tests/golden_output/aidl-test-interface-java-source/gen/android/aidl/tests/RecursiveList.java
+++ b/tests/golden_output/aidl-test-interface-java-source/gen/android/aidl/tests/RecursiveList.java
@@ -23,7 +23,7 @@
     int _aidl_start_pos = _aidl_parcel.dataPosition();
     _aidl_parcel.writeInt(0);
     _aidl_parcel.writeInt(value);
-    _aidl_parcel.writeTypedObject(next, 0);
+    _aidl_parcel.writeTypedObject(next, _aidl_flag);
     int _aidl_end_pos = _aidl_parcel.dataPosition();
     _aidl_parcel.setDataPosition(_aidl_start_pos);
     _aidl_parcel.writeInt(_aidl_end_pos - _aidl_start_pos);
diff --git a/tests/golden_output/aidl-test-interface-java-source/gen/android/aidl/tests/StructuredParcelable.java b/tests/golden_output/aidl-test-interface-java-source/gen/android/aidl/tests/StructuredParcelable.java
index b90fe2c..fa34aff 100644
--- a/tests/golden_output/aidl-test-interface-java-source/gen/android/aidl/tests/StructuredParcelable.java
+++ b/tests/golden_output/aidl-test-interface-java-source/gen/android/aidl/tests/StructuredParcelable.java
@@ -112,7 +112,7 @@
     _aidl_parcel.writeLong(int64_max);
     _aidl_parcel.writeInt(hexInt32_neg_1);
     _aidl_parcel.writeStrongBinder(ibinder);
-    _aidl_parcel.writeTypedObject(empty, 0);
+    _aidl_parcel.writeTypedObject(empty, _aidl_flag);
     _aidl_parcel.writeByteArray(int8_1);
     _aidl_parcel.writeIntArray(int32_1);
     _aidl_parcel.writeLongArray(int64_1);
@@ -131,8 +131,8 @@
     _aidl_parcel.writeString(addString1);
     _aidl_parcel.writeString(addString2);
     _aidl_parcel.writeInt(shouldSetBit0AndBit2);
-    _aidl_parcel.writeTypedObject(u, 0);
-    _aidl_parcel.writeTypedObject(shouldBeConstS1, 0);
+    _aidl_parcel.writeTypedObject(u, _aidl_flag);
+    _aidl_parcel.writeTypedObject(shouldBeConstS1, _aidl_flag);
     _aidl_parcel.writeInt(defaultWithFoo);
     int _aidl_end_pos = _aidl_parcel.dataPosition();
     _aidl_parcel.setDataPosition(_aidl_start_pos);
@@ -272,10 +272,10 @@
     _aidl_sj.add("f: " + (f));
     _aidl_sj.add("shouldBeJerry: " + (java.util.Objects.toString(shouldBeJerry)));
     _aidl_sj.add("shouldBeByteBar: " + (shouldBeByteBar));
-    _aidl_sj.add("shouldBeIntBar: " + (shouldBeIntBar));
+    _aidl_sj.add("shouldBeIntBar: " + (android.aidl.tests.IntEnum.$.toString(shouldBeIntBar)));
     _aidl_sj.add("shouldBeLongBar: " + (shouldBeLongBar));
     _aidl_sj.add("shouldContainTwoByteFoos: " + (java.util.Arrays.toString(shouldContainTwoByteFoos)));
-    _aidl_sj.add("shouldContainTwoIntFoos: " + (java.util.Arrays.toString(shouldContainTwoIntFoos)));
+    _aidl_sj.add("shouldContainTwoIntFoos: " + (android.aidl.tests.IntEnum.$.arrayToString(shouldContainTwoIntFoos)));
     _aidl_sj.add("shouldContainTwoLongFoos: " + (java.util.Arrays.toString(shouldContainTwoLongFoos)));
     _aidl_sj.add("stringDefaultsToFoo: " + (java.util.Objects.toString(stringDefaultsToFoo)));
     _aidl_sj.add("byteDefaultsToFour: " + (byteDefaultsToFour));
@@ -322,7 +322,7 @@
     _aidl_sj.add("shouldSetBit0AndBit2: " + (shouldSetBit0AndBit2));
     _aidl_sj.add("u: " + (java.util.Objects.toString(u)));
     _aidl_sj.add("shouldBeConstS1: " + (java.util.Objects.toString(shouldBeConstS1)));
-    _aidl_sj.add("defaultWithFoo: " + (defaultWithFoo));
+    _aidl_sj.add("defaultWithFoo: " + (android.aidl.tests.IntEnum.$.toString(defaultWithFoo)));
     return "android.aidl.tests.StructuredParcelable" + _aidl_sj.toString()  ;
   }
   @Override
diff --git a/tests/golden_output/aidl-test-interface-java-source/gen/android/aidl/tests/Union.java b/tests/golden_output/aidl-test-interface-java-source/gen/android/aidl/tests/Union.java
index 50688a6..018898a 100644
--- a/tests/golden_output/aidl-test-interface-java-source/gen/android/aidl/tests/Union.java
+++ b/tests/golden_output/aidl-test-interface-java-source/gen/android/aidl/tests/Union.java
@@ -283,4 +283,13 @@
     this._tag = _tag;
     this._value = _value;
   }
+  public static @interface Tag {
+    public static final int ns = 0;
+    public static final int n = 1;
+    public static final int m = 2;
+    public static final int s = 3;
+    public static final int ibinder = 4;
+    public static final int ss = 5;
+    public static final int be = 6;
+  }
 }
diff --git a/tests/golden_output/aidl-test-interface-java-source/gen/android/aidl/tests/UnionWithFd.java b/tests/golden_output/aidl-test-interface-java-source/gen/android/aidl/tests/UnionWithFd.java
index 941c947..63a5f1a 100644
--- a/tests/golden_output/aidl-test-interface-java-source/gen/android/aidl/tests/UnionWithFd.java
+++ b/tests/golden_output/aidl-test-interface-java-source/gen/android/aidl/tests/UnionWithFd.java
@@ -78,7 +78,7 @@
       _aidl_parcel.writeInt(getNum());
       break;
     case pfd:
-      _aidl_parcel.writeTypedObject(getPfd(), 0);
+      _aidl_parcel.writeTypedObject(getPfd(), _aidl_flag);
       break;
     }
   }
@@ -137,4 +137,8 @@
     this._tag = _tag;
     this._value = _value;
   }
+  public static @interface Tag {
+    public static final int num = 0;
+    public static final int pfd = 1;
+  }
 }
diff --git a/tests/golden_output/aidl-test-interface-java-source/gen/android/aidl/tests/extension/MyExt2.java b/tests/golden_output/aidl-test-interface-java-source/gen/android/aidl/tests/extension/MyExt2.java
index 54acb1f..8d3bf56 100644
--- a/tests/golden_output/aidl-test-interface-java-source/gen/android/aidl/tests/extension/MyExt2.java
+++ b/tests/golden_output/aidl-test-interface-java-source/gen/android/aidl/tests/extension/MyExt2.java
@@ -24,7 +24,7 @@
     int _aidl_start_pos = _aidl_parcel.dataPosition();
     _aidl_parcel.writeInt(0);
     _aidl_parcel.writeInt(a);
-    _aidl_parcel.writeTypedObject(b, 0);
+    _aidl_parcel.writeTypedObject(b, _aidl_flag);
     _aidl_parcel.writeString(c);
     int _aidl_end_pos = _aidl_parcel.dataPosition();
     _aidl_parcel.setDataPosition(_aidl_start_pos);
diff --git a/tests/golden_output/aidl-test-interface-java-source/gen/android/aidl/tests/permission/IProtected.java.d b/tests/golden_output/aidl-test-interface-java-source/gen/android/aidl/tests/permission/IProtected.java.d
deleted file mode 100644
index 9eb7054..0000000
--- a/tests/golden_output/aidl-test-interface-java-source/gen/android/aidl/tests/permission/IProtected.java.d
+++ /dev/null
@@ -1,2 +0,0 @@
-out/soong/.intermediates/system/tools/aidl/aidl-test-interface-java-source/gen/android/aidl/tests/permission/IProtected.java : \
-  system/tools/aidl/tests/android/aidl/tests/permission/IProtected.aidl
diff --git a/tests/golden_output/aidl-test-interface-java-source/gen/android/aidl/tests/permission/IProtectedInterface.java.d b/tests/golden_output/aidl-test-interface-java-source/gen/android/aidl/tests/permission/IProtectedInterface.java.d
deleted file mode 100644
index 4f04f76..0000000
--- a/tests/golden_output/aidl-test-interface-java-source/gen/android/aidl/tests/permission/IProtectedInterface.java.d
+++ /dev/null
@@ -1,2 +0,0 @@
-out/soong/.intermediates/system/tools/aidl/aidl-test-interface-java-source/gen/android/aidl/tests/permission/IProtectedInterface.java : \
-  system/tools/aidl/tests/android/aidl/tests/permission/IProtectedInterface.aidl
diff --git a/tests/golden_output/aidl-test-interface-java-source/gen/android/aidl/tests/unions/EnumUnion.java b/tests/golden_output/aidl-test-interface-java-source/gen/android/aidl/tests/unions/EnumUnion.java
index 28df6de..7519fd4 100644
--- a/tests/golden_output/aidl-test-interface-java-source/gen/android/aidl/tests/unions/EnumUnion.java
+++ b/tests/golden_output/aidl-test-interface-java-source/gen/android/aidl/tests/unions/EnumUnion.java
@@ -6,6 +6,7 @@
   // tags for union fields
   public final static int intEnum = 0;  // android.aidl.tests.IntEnum intEnum;
   public final static int longEnum = 1;  // android.aidl.tests.LongEnum longEnum;
+  public final static int deprecatedField = 2;  // int deprecatedField;
 
   private int _tag;
   private Object _value;
@@ -59,6 +60,23 @@
     _set(longEnum, _value);
   }
 
+  // int deprecatedField;
+
+  /** @deprecated do not use this */
+  @Deprecated
+  public static EnumUnion deprecatedField(int _value) {
+    return new EnumUnion(deprecatedField, _value);
+  }
+
+  public int getDeprecatedField() {
+    _assertTag(deprecatedField);
+    return (int) _value;
+  }
+
+  public void setDeprecatedField(int _value) {
+    _set(deprecatedField, _value);
+  }
+
   public static final android.os.Parcelable.Creator<EnumUnion> CREATOR = new android.os.Parcelable.Creator<EnumUnion>() {
     @Override
     public EnumUnion createFromParcel(android.os.Parcel _aidl_source) {
@@ -80,6 +98,9 @@
     case longEnum:
       _aidl_parcel.writeLong(getLongEnum());
       break;
+    case deprecatedField:
+      _aidl_parcel.writeInt(getDeprecatedField());
+      break;
     }
   }
 
@@ -97,6 +118,11 @@
       _aidl_value = _aidl_parcel.readLong();
       _set(_aidl_tag, _aidl_value);
       return; }
+    case deprecatedField: {
+      int _aidl_value;
+      _aidl_value = _aidl_parcel.readInt();
+      _set(_aidl_tag, _aidl_value);
+      return; }
     }
     throw new IllegalArgumentException("union: unknown tag: " + _aidl_tag);
   }
@@ -112,8 +138,9 @@
   @Override
   public String toString() {
     switch (_tag) {
-    case intEnum: return "android.aidl.tests.unions.EnumUnion.intEnum(" + (getIntEnum()) + ")";
+    case intEnum: return "android.aidl.tests.unions.EnumUnion.intEnum(" + (android.aidl.tests.IntEnum.$.toString(getIntEnum())) + ")";
     case longEnum: return "android.aidl.tests.unions.EnumUnion.longEnum(" + (getLongEnum()) + ")";
+    case deprecatedField: return "android.aidl.tests.unions.EnumUnion.deprecatedField(" + (getDeprecatedField()) + ")";
     }
     throw new IllegalStateException("unknown field: " + _tag);
   }
@@ -143,6 +170,7 @@
     switch (_tag) {
     case intEnum: return "intEnum";
     case longEnum: return "longEnum";
+    case deprecatedField: return "deprecatedField";
     }
     throw new IllegalStateException("unknown field: " + _tag);
   }
@@ -151,4 +179,11 @@
     this._tag = _tag;
     this._value = _value;
   }
+  public static @interface Tag {
+    public static final int intEnum = 0;
+    public static final int longEnum = 1;
+    /** @deprecated do not use this */
+    @Deprecated
+    public static final int deprecatedField = 2;
+  }
 }
diff --git a/tests/golden_output/aidl-test-interface-java-source/gen/android/aidl/tests/unions/UnionInUnion.java b/tests/golden_output/aidl-test-interface-java-source/gen/android/aidl/tests/unions/UnionInUnion.java
index 241511e..4653900 100644
--- a/tests/golden_output/aidl-test-interface-java-source/gen/android/aidl/tests/unions/UnionInUnion.java
+++ b/tests/golden_output/aidl-test-interface-java-source/gen/android/aidl/tests/unions/UnionInUnion.java
@@ -75,7 +75,7 @@
     _aidl_parcel.writeInt(_tag);
     switch (_tag) {
     case first:
-      _aidl_parcel.writeTypedObject(getFirst(), 0);
+      _aidl_parcel.writeTypedObject(getFirst(), _aidl_flag);
       break;
     case second:
       _aidl_parcel.writeInt(getSecond());
@@ -161,4 +161,8 @@
     this._tag = _tag;
     this._value = _value;
   }
+  public static @interface Tag {
+    public static final int first = 0;
+    public static final int second = 1;
+  }
 }
diff --git a/tests/golden_output/aidl-test-interface-ndk-source/gen/android/aidl/tests/ArrayOfInterfaces.cpp b/tests/golden_output/aidl-test-interface-ndk-source/gen/android/aidl/tests/ArrayOfInterfaces.cpp
index e3f691a..c42c372 100644
--- a/tests/golden_output/aidl-test-interface-ndk-source/gen/android/aidl/tests/ArrayOfInterfaces.cpp
+++ b/tests/golden_output/aidl-test-interface-ndk-source/gen/android/aidl/tests/ArrayOfInterfaces.cpp
@@ -415,7 +415,7 @@
   binder_status_t _aidl_ret_status;
   int32_t _aidl_tag;
   if ((_aidl_ret_status = ::ndk::AParcel_readData(_parcel, &_aidl_tag)) != STATUS_OK) return _aidl_ret_status;
-  switch (_aidl_tag) {
+  switch (static_cast<Tag>(_aidl_tag)) {
   case iface: {
     std::shared_ptr<::aidl::android::aidl::tests::ArrayOfInterfaces::IEmptyInterface> _aidl_value;
     if ((_aidl_ret_status = ::ndk::AParcel_readData(_parcel, &_aidl_value)) != STATUS_OK) return _aidl_ret_status;
@@ -460,7 +460,7 @@
   return STATUS_BAD_VALUE;
 }
 binder_status_t ArrayOfInterfaces::MyUnion::writeToParcel(AParcel* _parcel) const {
-  binder_status_t _aidl_ret_status = ::ndk::AParcel_writeData(_parcel, getTag());
+  binder_status_t _aidl_ret_status = ::ndk::AParcel_writeData(_parcel, static_cast<int32_t>(getTag()));
   if (_aidl_ret_status != STATUS_OK) return _aidl_ret_status;
   switch (getTag()) {
   case iface: return ::ndk::AParcel_writeData(_parcel, get<iface>());
diff --git a/tests/golden_output/aidl-test-interface-ndk-source/gen/android/aidl/tests/FixedSize.cpp b/tests/golden_output/aidl-test-interface-ndk-source/gen/android/aidl/tests/FixedSize.cpp
index b5a6115..689a388 100644
--- a/tests/golden_output/aidl-test-interface-ndk-source/gen/android/aidl/tests/FixedSize.cpp
+++ b/tests/golden_output/aidl-test-interface-ndk-source/gen/android/aidl/tests/FixedSize.cpp
@@ -172,7 +172,7 @@
   binder_status_t _aidl_ret_status;
   int32_t _aidl_tag;
   if ((_aidl_ret_status = ::ndk::AParcel_readData(_parcel, &_aidl_tag)) != STATUS_OK) return _aidl_ret_status;
-  switch (_aidl_tag) {
+  switch (static_cast<Tag>(_aidl_tag)) {
   case booleanValue: {
     bool _aidl_value;
     if ((_aidl_ret_status = ::ndk::AParcel_readData(_parcel, &_aidl_value)) != STATUS_OK) return _aidl_ret_status;
@@ -257,7 +257,7 @@
   return STATUS_BAD_VALUE;
 }
 binder_status_t FixedSize::FixedUnion::writeToParcel(AParcel* _parcel) const {
-  binder_status_t _aidl_ret_status = ::ndk::AParcel_writeData(_parcel, getTag());
+  binder_status_t _aidl_ret_status = ::ndk::AParcel_writeData(_parcel, static_cast<int32_t>(getTag()));
   if (_aidl_ret_status != STATUS_OK) return _aidl_ret_status;
   switch (getTag()) {
   case booleanValue: return ::ndk::AParcel_writeData(_parcel, get<booleanValue>());
diff --git a/tests/golden_output/aidl-test-interface-ndk-source/gen/android/aidl/tests/ITestService.cpp b/tests/golden_output/aidl-test-interface-ndk-source/gen/android/aidl/tests/ITestService.cpp
index 0268aaa..8c9ff0a 100644
--- a/tests/golden_output/aidl-test-interface-ndk-source/gen/android/aidl/tests/ITestService.cpp
+++ b/tests/golden_output/aidl-test-interface-ndk-source/gen/android/aidl/tests/ITestService.cpp
@@ -1246,7 +1246,25 @@
 
       break;
     }
-    case (FIRST_CALL_TRANSACTION + 63 /*GetCppJavaTests*/): {
+    case (FIRST_CALL_TRANSACTION + 63 /*GetUnionTags*/): {
+      std::vector<::aidl::android::aidl::tests::Union> in_input;
+      std::vector<::aidl::android::aidl::tests::Union::Tag> _aidl_return;
+
+      _aidl_ret_status = ::ndk::AParcel_readData(_aidl_in, &in_input);
+      if (_aidl_ret_status != STATUS_OK) break;
+
+      ::ndk::ScopedAStatus _aidl_status = _aidl_impl->GetUnionTags(in_input, &_aidl_return);
+      _aidl_ret_status = AParcel_writeStatusHeader(_aidl_out, _aidl_status.get());
+      if (_aidl_ret_status != STATUS_OK) break;
+
+      if (!AStatus_isOk(_aidl_status.get())) break;
+
+      _aidl_ret_status = ::ndk::AParcel_writeData(_aidl_out, _aidl_return);
+      if (_aidl_ret_status != STATUS_OK) break;
+
+      break;
+    }
+    case (FIRST_CALL_TRANSACTION + 64 /*GetCppJavaTests*/): {
       ::ndk::SpAIBinder _aidl_return;
 
       ::ndk::ScopedAStatus _aidl_status = _aidl_impl->GetCppJavaTests(&_aidl_return);
@@ -1260,7 +1278,7 @@
 
       break;
     }
-    case (FIRST_CALL_TRANSACTION + 64 /*getBackendType*/): {
+    case (FIRST_CALL_TRANSACTION + 65 /*getBackendType*/): {
       ::aidl::android::aidl::tests::BackendType _aidl_return;
 
       ::ndk::ScopedAStatus _aidl_status = _aidl_impl->getBackendType(&_aidl_return);
@@ -3944,6 +3962,47 @@
   _aidl_status_return:
   return _aidl_status;
 }
+::ndk::ScopedAStatus BpTestService::GetUnionTags(const std::vector<::aidl::android::aidl::tests::Union>& in_input, std::vector<::aidl::android::aidl::tests::Union::Tag>* _aidl_return) {
+  binder_status_t _aidl_ret_status = STATUS_OK;
+  ::ndk::ScopedAStatus _aidl_status;
+  ::ndk::ScopedAParcel _aidl_in;
+  ::ndk::ScopedAParcel _aidl_out;
+
+  _aidl_ret_status = AIBinder_prepareTransaction(asBinder().get(), _aidl_in.getR());
+  AParcel_markSensitive(_aidl_in.get());
+  if (_aidl_ret_status != STATUS_OK) goto _aidl_error;
+
+  _aidl_ret_status = ::ndk::AParcel_writeData(_aidl_in.get(), in_input);
+  if (_aidl_ret_status != STATUS_OK) goto _aidl_error;
+
+  _aidl_ret_status = AIBinder_transact(
+    asBinder().get(),
+    (FIRST_CALL_TRANSACTION + 63 /*GetUnionTags*/),
+    _aidl_in.getR(),
+    _aidl_out.getR(),
+    FLAG_CLEAR_BUF
+    #ifdef BINDER_STABILITY_SUPPORT
+    | FLAG_PRIVATE_LOCAL
+    #endif  // BINDER_STABILITY_SUPPORT
+    );
+  if (_aidl_ret_status == STATUS_UNKNOWN_TRANSACTION && ITestService::getDefaultImpl()) {
+    _aidl_status = ITestService::getDefaultImpl()->GetUnionTags(in_input, _aidl_return);
+    goto _aidl_status_return;
+  }
+  if (_aidl_ret_status != STATUS_OK) goto _aidl_error;
+
+  _aidl_ret_status = AParcel_readStatusHeader(_aidl_out.get(), _aidl_status.getR());
+  if (_aidl_ret_status != STATUS_OK) goto _aidl_error;
+
+  if (!AStatus_isOk(_aidl_status.get())) goto _aidl_status_return;
+  _aidl_ret_status = ::ndk::AParcel_readData(_aidl_out.get(), _aidl_return);
+  if (_aidl_ret_status != STATUS_OK) goto _aidl_error;
+
+  _aidl_error:
+  _aidl_status.set(AStatus_fromStatus(_aidl_ret_status));
+  _aidl_status_return:
+  return _aidl_status;
+}
 ::ndk::ScopedAStatus BpTestService::GetCppJavaTests(::ndk::SpAIBinder* _aidl_return) {
   binder_status_t _aidl_ret_status = STATUS_OK;
   ::ndk::ScopedAStatus _aidl_status;
@@ -3956,7 +4015,7 @@
 
   _aidl_ret_status = AIBinder_transact(
     asBinder().get(),
-    (FIRST_CALL_TRANSACTION + 63 /*GetCppJavaTests*/),
+    (FIRST_CALL_TRANSACTION + 64 /*GetCppJavaTests*/),
     _aidl_in.getR(),
     _aidl_out.getR(),
     FLAG_CLEAR_BUF
@@ -3994,7 +4053,7 @@
 
   _aidl_ret_status = AIBinder_transact(
     asBinder().get(),
-    (FIRST_CALL_TRANSACTION + 64 /*getBackendType*/),
+    (FIRST_CALL_TRANSACTION + 65 /*getBackendType*/),
     _aidl_in.getR(),
     _aidl_out.getR(),
     FLAG_CLEAR_BUF
@@ -4388,6 +4447,11 @@
   _aidl_status.set(AStatus_fromStatus(STATUS_UNKNOWN_TRANSACTION));
   return _aidl_status;
 }
+::ndk::ScopedAStatus ITestServiceDefault::GetUnionTags(const std::vector<::aidl::android::aidl::tests::Union>& /*in_input*/, std::vector<::aidl::android::aidl::tests::Union::Tag>* /*_aidl_return*/) {
+  ::ndk::ScopedAStatus _aidl_status;
+  _aidl_status.set(AStatus_fromStatus(STATUS_UNKNOWN_TRANSACTION));
+  return _aidl_status;
+}
 ::ndk::ScopedAStatus ITestServiceDefault::GetCppJavaTests(::ndk::SpAIBinder* /*_aidl_return*/) {
   ::ndk::ScopedAStatus _aidl_status;
   _aidl_status.set(AStatus_fromStatus(STATUS_UNKNOWN_TRANSACTION));
diff --git a/tests/golden_output/aidl-test-interface-ndk-source/gen/android/aidl/tests/ITestService.cpp.d b/tests/golden_output/aidl-test-interface-ndk-source/gen/android/aidl/tests/ITestService.cpp.d
index b46066a..19acd9f 100644
--- a/tests/golden_output/aidl-test-interface-ndk-source/gen/android/aidl/tests/ITestService.cpp.d
+++ b/tests/golden_output/aidl-test-interface-ndk-source/gen/android/aidl/tests/ITestService.cpp.d
@@ -9,6 +9,6 @@
   system/tools/aidl/tests/android/aidl/tests/LongEnum.aidl \
   system/tools/aidl/tests/android/aidl/tests/RecursiveList.aidl \
   system/tools/aidl/tests/android/aidl/tests/StructuredParcelable.aidl \
+  system/tools/aidl/tests/android/aidl/tests/Union.aidl \
   system/tools/aidl/tests/android/aidl/tests/extension/ExtendableParcelable.aidl \
-  system/tools/aidl/tests/android/aidl/tests/ConstantExpressionEnum.aidl \
-  system/tools/aidl/tests/android/aidl/tests/Union.aidl
+  system/tools/aidl/tests/android/aidl/tests/ConstantExpressionEnum.aidl
diff --git a/tests/golden_output/aidl-test-interface-ndk-source/gen/android/aidl/tests/ListOfInterfaces.cpp b/tests/golden_output/aidl-test-interface-ndk-source/gen/android/aidl/tests/ListOfInterfaces.cpp
index e1eda40..cd90a13 100644
--- a/tests/golden_output/aidl-test-interface-ndk-source/gen/android/aidl/tests/ListOfInterfaces.cpp
+++ b/tests/golden_output/aidl-test-interface-ndk-source/gen/android/aidl/tests/ListOfInterfaces.cpp
@@ -403,7 +403,7 @@
   binder_status_t _aidl_ret_status;
   int32_t _aidl_tag;
   if ((_aidl_ret_status = ::ndk::AParcel_readData(_parcel, &_aidl_tag)) != STATUS_OK) return _aidl_ret_status;
-  switch (_aidl_tag) {
+  switch (static_cast<Tag>(_aidl_tag)) {
   case iface: {
     std::shared_ptr<::aidl::android::aidl::tests::ListOfInterfaces::IEmptyInterface> _aidl_value;
     if ((_aidl_ret_status = ::ndk::AParcel_readData(_parcel, &_aidl_value)) != STATUS_OK) return _aidl_ret_status;
@@ -448,7 +448,7 @@
   return STATUS_BAD_VALUE;
 }
 binder_status_t ListOfInterfaces::MyUnion::writeToParcel(AParcel* _parcel) const {
-  binder_status_t _aidl_ret_status = ::ndk::AParcel_writeData(_parcel, getTag());
+  binder_status_t _aidl_ret_status = ::ndk::AParcel_writeData(_parcel, static_cast<int32_t>(getTag()));
   if (_aidl_ret_status != STATUS_OK) return _aidl_ret_status;
   switch (getTag()) {
   case iface: return ::ndk::AParcel_writeData(_parcel, get<iface>());
diff --git a/tests/golden_output/aidl-test-interface-ndk-source/gen/android/aidl/tests/Union.cpp b/tests/golden_output/aidl-test-interface-ndk-source/gen/android/aidl/tests/Union.cpp
index 6941c03..a8989bc 100644
--- a/tests/golden_output/aidl-test-interface-ndk-source/gen/android/aidl/tests/Union.cpp
+++ b/tests/golden_output/aidl-test-interface-ndk-source/gen/android/aidl/tests/Union.cpp
@@ -13,7 +13,7 @@
   binder_status_t _aidl_ret_status;
   int32_t _aidl_tag;
   if ((_aidl_ret_status = ::ndk::AParcel_readData(_parcel, &_aidl_tag)) != STATUS_OK) return _aidl_ret_status;
-  switch (_aidl_tag) {
+  switch (static_cast<Tag>(_aidl_tag)) {
   case ns: {
     std::vector<int32_t> _aidl_value;
     if ((_aidl_ret_status = ::ndk::AParcel_readData(_parcel, &_aidl_value)) != STATUS_OK) return _aidl_ret_status;
@@ -88,7 +88,7 @@
   return STATUS_BAD_VALUE;
 }
 binder_status_t Union::writeToParcel(AParcel* _parcel) const {
-  binder_status_t _aidl_ret_status = ::ndk::AParcel_writeData(_parcel, getTag());
+  binder_status_t _aidl_ret_status = ::ndk::AParcel_writeData(_parcel, static_cast<int32_t>(getTag()));
   if (_aidl_ret_status != STATUS_OK) return _aidl_ret_status;
   switch (getTag()) {
   case ns: return ::ndk::AParcel_writeData(_parcel, get<ns>());
diff --git a/tests/golden_output/aidl-test-interface-ndk-source/gen/android/aidl/tests/UnionWithFd.cpp b/tests/golden_output/aidl-test-interface-ndk-source/gen/android/aidl/tests/UnionWithFd.cpp
index 223d64c..1eefc3e 100644
--- a/tests/golden_output/aidl-test-interface-ndk-source/gen/android/aidl/tests/UnionWithFd.cpp
+++ b/tests/golden_output/aidl-test-interface-ndk-source/gen/android/aidl/tests/UnionWithFd.cpp
@@ -12,7 +12,7 @@
   binder_status_t _aidl_ret_status;
   int32_t _aidl_tag;
   if ((_aidl_ret_status = ::ndk::AParcel_readData(_parcel, &_aidl_tag)) != STATUS_OK) return _aidl_ret_status;
-  switch (_aidl_tag) {
+  switch (static_cast<Tag>(_aidl_tag)) {
   case num: {
     int32_t _aidl_value;
     if ((_aidl_ret_status = ::ndk::AParcel_readData(_parcel, &_aidl_value)) != STATUS_OK) return _aidl_ret_status;
@@ -37,7 +37,7 @@
   return STATUS_BAD_VALUE;
 }
 binder_status_t UnionWithFd::writeToParcel(AParcel* _parcel) const {
-  binder_status_t _aidl_ret_status = ::ndk::AParcel_writeData(_parcel, getTag());
+  binder_status_t _aidl_ret_status = ::ndk::AParcel_writeData(_parcel, static_cast<int32_t>(getTag()));
   if (_aidl_ret_status != STATUS_OK) return _aidl_ret_status;
   switch (getTag()) {
   case num: return ::ndk::AParcel_writeData(_parcel, get<num>());
diff --git a/tests/golden_output/aidl-test-interface-ndk-source/gen/android/aidl/tests/permission/IProtected.cpp b/tests/golden_output/aidl-test-interface-ndk-source/gen/android/aidl/tests/permission/IProtected.cpp
deleted file mode 100644
index 15e7a78..0000000
--- a/tests/golden_output/aidl-test-interface-ndk-source/gen/android/aidl/tests/permission/IProtected.cpp
+++ /dev/null
@@ -1,234 +0,0 @@
-#include "aidl/android/aidl/tests/permission/IProtected.h"
-
-#include <android/binder_parcel_utils.h>
-#include <aidl/android/aidl/tests/permission/BnProtected.h>
-#include <aidl/android/aidl/tests/permission/BpProtected.h>
-
-namespace aidl {
-namespace android {
-namespace aidl {
-namespace tests {
-namespace permission {
-static binder_status_t _aidl_android_aidl_tests_permission_IProtected_onTransact(AIBinder* _aidl_binder, transaction_code_t _aidl_code, const AParcel* _aidl_in, AParcel* _aidl_out) {
-  (void)_aidl_in;
-  (void)_aidl_out;
-  binder_status_t _aidl_ret_status = STATUS_UNKNOWN_TRANSACTION;
-  std::shared_ptr<BnProtected> _aidl_impl = std::static_pointer_cast<BnProtected>(::ndk::ICInterface::asInterface(_aidl_binder));
-  switch (_aidl_code) {
-    case (FIRST_CALL_TRANSACTION + 0 /*PermissionProtected*/): {
-
-      ::ndk::ScopedAStatus _aidl_status = _aidl_impl->PermissionProtected();
-      _aidl_ret_status = AParcel_writeStatusHeader(_aidl_out, _aidl_status.get());
-      if (_aidl_ret_status != STATUS_OK) break;
-
-      if (!AStatus_isOk(_aidl_status.get())) break;
-
-      break;
-    }
-    case (FIRST_CALL_TRANSACTION + 1 /*MultiplePermissionsAll*/): {
-
-      ::ndk::ScopedAStatus _aidl_status = _aidl_impl->MultiplePermissionsAll();
-      _aidl_ret_status = AParcel_writeStatusHeader(_aidl_out, _aidl_status.get());
-      if (_aidl_ret_status != STATUS_OK) break;
-
-      if (!AStatus_isOk(_aidl_status.get())) break;
-
-      break;
-    }
-    case (FIRST_CALL_TRANSACTION + 2 /*MultiplePermissionsAny*/): {
-
-      ::ndk::ScopedAStatus _aidl_status = _aidl_impl->MultiplePermissionsAny();
-      _aidl_ret_status = AParcel_writeStatusHeader(_aidl_out, _aidl_status.get());
-      if (_aidl_ret_status != STATUS_OK) break;
-
-      if (!AStatus_isOk(_aidl_status.get())) break;
-
-      break;
-    }
-  }
-  return _aidl_ret_status;
-}
-
-static AIBinder_Class* _g_aidl_android_aidl_tests_permission_IProtected_clazz = ::ndk::ICInterface::defineClass(IProtected::descriptor, _aidl_android_aidl_tests_permission_IProtected_onTransact);
-
-BpProtected::BpProtected(const ::ndk::SpAIBinder& binder) : BpCInterface(binder) {}
-BpProtected::~BpProtected() {}
-
-::ndk::ScopedAStatus BpProtected::PermissionProtected() {
-  binder_status_t _aidl_ret_status = STATUS_OK;
-  ::ndk::ScopedAStatus _aidl_status;
-  ::ndk::ScopedAParcel _aidl_in;
-  ::ndk::ScopedAParcel _aidl_out;
-
-  _aidl_ret_status = AIBinder_prepareTransaction(asBinder().get(), _aidl_in.getR());
-  if (_aidl_ret_status != STATUS_OK) goto _aidl_error;
-
-  _aidl_ret_status = AIBinder_transact(
-    asBinder().get(),
-    (FIRST_CALL_TRANSACTION + 0 /*PermissionProtected*/),
-    _aidl_in.getR(),
-    _aidl_out.getR(),
-    0
-    #ifdef BINDER_STABILITY_SUPPORT
-    | FLAG_PRIVATE_LOCAL
-    #endif  // BINDER_STABILITY_SUPPORT
-    );
-  if (_aidl_ret_status == STATUS_UNKNOWN_TRANSACTION && IProtected::getDefaultImpl()) {
-    _aidl_status = IProtected::getDefaultImpl()->PermissionProtected();
-    goto _aidl_status_return;
-  }
-  if (_aidl_ret_status != STATUS_OK) goto _aidl_error;
-
-  _aidl_ret_status = AParcel_readStatusHeader(_aidl_out.get(), _aidl_status.getR());
-  if (_aidl_ret_status != STATUS_OK) goto _aidl_error;
-
-  if (!AStatus_isOk(_aidl_status.get())) goto _aidl_status_return;
-  _aidl_error:
-  _aidl_status.set(AStatus_fromStatus(_aidl_ret_status));
-  _aidl_status_return:
-  return _aidl_status;
-}
-::ndk::ScopedAStatus BpProtected::MultiplePermissionsAll() {
-  binder_status_t _aidl_ret_status = STATUS_OK;
-  ::ndk::ScopedAStatus _aidl_status;
-  ::ndk::ScopedAParcel _aidl_in;
-  ::ndk::ScopedAParcel _aidl_out;
-
-  _aidl_ret_status = AIBinder_prepareTransaction(asBinder().get(), _aidl_in.getR());
-  if (_aidl_ret_status != STATUS_OK) goto _aidl_error;
-
-  _aidl_ret_status = AIBinder_transact(
-    asBinder().get(),
-    (FIRST_CALL_TRANSACTION + 1 /*MultiplePermissionsAll*/),
-    _aidl_in.getR(),
-    _aidl_out.getR(),
-    0
-    #ifdef BINDER_STABILITY_SUPPORT
-    | FLAG_PRIVATE_LOCAL
-    #endif  // BINDER_STABILITY_SUPPORT
-    );
-  if (_aidl_ret_status == STATUS_UNKNOWN_TRANSACTION && IProtected::getDefaultImpl()) {
-    _aidl_status = IProtected::getDefaultImpl()->MultiplePermissionsAll();
-    goto _aidl_status_return;
-  }
-  if (_aidl_ret_status != STATUS_OK) goto _aidl_error;
-
-  _aidl_ret_status = AParcel_readStatusHeader(_aidl_out.get(), _aidl_status.getR());
-  if (_aidl_ret_status != STATUS_OK) goto _aidl_error;
-
-  if (!AStatus_isOk(_aidl_status.get())) goto _aidl_status_return;
-  _aidl_error:
-  _aidl_status.set(AStatus_fromStatus(_aidl_ret_status));
-  _aidl_status_return:
-  return _aidl_status;
-}
-::ndk::ScopedAStatus BpProtected::MultiplePermissionsAny() {
-  binder_status_t _aidl_ret_status = STATUS_OK;
-  ::ndk::ScopedAStatus _aidl_status;
-  ::ndk::ScopedAParcel _aidl_in;
-  ::ndk::ScopedAParcel _aidl_out;
-
-  _aidl_ret_status = AIBinder_prepareTransaction(asBinder().get(), _aidl_in.getR());
-  if (_aidl_ret_status != STATUS_OK) goto _aidl_error;
-
-  _aidl_ret_status = AIBinder_transact(
-    asBinder().get(),
-    (FIRST_CALL_TRANSACTION + 2 /*MultiplePermissionsAny*/),
-    _aidl_in.getR(),
-    _aidl_out.getR(),
-    0
-    #ifdef BINDER_STABILITY_SUPPORT
-    | FLAG_PRIVATE_LOCAL
-    #endif  // BINDER_STABILITY_SUPPORT
-    );
-  if (_aidl_ret_status == STATUS_UNKNOWN_TRANSACTION && IProtected::getDefaultImpl()) {
-    _aidl_status = IProtected::getDefaultImpl()->MultiplePermissionsAny();
-    goto _aidl_status_return;
-  }
-  if (_aidl_ret_status != STATUS_OK) goto _aidl_error;
-
-  _aidl_ret_status = AParcel_readStatusHeader(_aidl_out.get(), _aidl_status.getR());
-  if (_aidl_ret_status != STATUS_OK) goto _aidl_error;
-
-  if (!AStatus_isOk(_aidl_status.get())) goto _aidl_status_return;
-  _aidl_error:
-  _aidl_status.set(AStatus_fromStatus(_aidl_ret_status));
-  _aidl_status_return:
-  return _aidl_status;
-}
-// Source for BnProtected
-BnProtected::BnProtected() {}
-BnProtected::~BnProtected() {}
-::ndk::SpAIBinder BnProtected::createBinder() {
-  AIBinder* binder = AIBinder_new(_g_aidl_android_aidl_tests_permission_IProtected_clazz, static_cast<void*>(this));
-  #ifdef BINDER_STABILITY_SUPPORT
-  AIBinder_markCompilationUnitStability(binder);
-  #endif  // BINDER_STABILITY_SUPPORT
-  return ::ndk::SpAIBinder(binder);
-}
-// Source for IProtected
-const char* IProtected::descriptor = "android.aidl.tests.permission.IProtected";
-IProtected::IProtected() {}
-IProtected::~IProtected() {}
-
-
-std::shared_ptr<IProtected> IProtected::fromBinder(const ::ndk::SpAIBinder& binder) {
-  if (!AIBinder_associateClass(binder.get(), _g_aidl_android_aidl_tests_permission_IProtected_clazz)) { return nullptr; }
-  std::shared_ptr<::ndk::ICInterface> interface = ::ndk::ICInterface::asInterface(binder.get());
-  if (interface) {
-    return std::static_pointer_cast<IProtected>(interface);
-  }
-  return ::ndk::SharedRefBase::make<BpProtected>(binder);
-}
-
-binder_status_t IProtected::writeToParcel(AParcel* parcel, const std::shared_ptr<IProtected>& instance) {
-  return AParcel_writeStrongBinder(parcel, instance ? instance->asBinder().get() : nullptr);
-}
-binder_status_t IProtected::readFromParcel(const AParcel* parcel, std::shared_ptr<IProtected>* instance) {
-  ::ndk::SpAIBinder binder;
-  binder_status_t status = AParcel_readStrongBinder(parcel, binder.getR());
-  if (status != STATUS_OK) return status;
-  *instance = IProtected::fromBinder(binder);
-  return STATUS_OK;
-}
-bool IProtected::setDefaultImpl(const std::shared_ptr<IProtected>& impl) {
-  // Only one user of this interface can use this function
-  // at a time. This is a heuristic to detect if two different
-  // users in the same process use this function.
-  assert(!IProtected::default_impl);
-  if (impl) {
-    IProtected::default_impl = impl;
-    return true;
-  }
-  return false;
-}
-const std::shared_ptr<IProtected>& IProtected::getDefaultImpl() {
-  return IProtected::default_impl;
-}
-std::shared_ptr<IProtected> IProtected::default_impl = nullptr;
-::ndk::ScopedAStatus IProtectedDefault::PermissionProtected() {
-  ::ndk::ScopedAStatus _aidl_status;
-  _aidl_status.set(AStatus_fromStatus(STATUS_UNKNOWN_TRANSACTION));
-  return _aidl_status;
-}
-::ndk::ScopedAStatus IProtectedDefault::MultiplePermissionsAll() {
-  ::ndk::ScopedAStatus _aidl_status;
-  _aidl_status.set(AStatus_fromStatus(STATUS_UNKNOWN_TRANSACTION));
-  return _aidl_status;
-}
-::ndk::ScopedAStatus IProtectedDefault::MultiplePermissionsAny() {
-  ::ndk::ScopedAStatus _aidl_status;
-  _aidl_status.set(AStatus_fromStatus(STATUS_UNKNOWN_TRANSACTION));
-  return _aidl_status;
-}
-::ndk::SpAIBinder IProtectedDefault::asBinder() {
-  return ::ndk::SpAIBinder();
-}
-bool IProtectedDefault::isRemote() {
-  return false;
-}
-}  // namespace permission
-}  // namespace tests
-}  // namespace aidl
-}  // namespace android
-}  // namespace aidl
diff --git a/tests/golden_output/aidl-test-interface-ndk-source/gen/android/aidl/tests/permission/IProtected.cpp.d b/tests/golden_output/aidl-test-interface-ndk-source/gen/android/aidl/tests/permission/IProtected.cpp.d
deleted file mode 100644
index 7db52de..0000000
--- a/tests/golden_output/aidl-test-interface-ndk-source/gen/android/aidl/tests/permission/IProtected.cpp.d
+++ /dev/null
@@ -1,2 +0,0 @@
-out/soong/.intermediates/system/tools/aidl/aidl-test-interface-ndk-source/gen/android/aidl/tests/permission/IProtected.cpp : \
-  system/tools/aidl/tests/android/aidl/tests/permission/IProtected.aidl
diff --git a/tests/golden_output/aidl-test-interface-ndk-source/gen/android/aidl/tests/permission/IProtectedInterface.cpp b/tests/golden_output/aidl-test-interface-ndk-source/gen/android/aidl/tests/permission/IProtectedInterface.cpp
deleted file mode 100644
index 454f6a4..0000000
--- a/tests/golden_output/aidl-test-interface-ndk-source/gen/android/aidl/tests/permission/IProtectedInterface.cpp
+++ /dev/null
@@ -1,185 +0,0 @@
-#include "aidl/android/aidl/tests/permission/IProtectedInterface.h"
-
-#include <android/binder_parcel_utils.h>
-#include <aidl/android/aidl/tests/permission/BnProtectedInterface.h>
-#include <aidl/android/aidl/tests/permission/BpProtectedInterface.h>
-
-namespace aidl {
-namespace android {
-namespace aidl {
-namespace tests {
-namespace permission {
-static binder_status_t _aidl_android_aidl_tests_permission_IProtectedInterface_onTransact(AIBinder* _aidl_binder, transaction_code_t _aidl_code, const AParcel* _aidl_in, AParcel* _aidl_out) {
-  (void)_aidl_in;
-  (void)_aidl_out;
-  binder_status_t _aidl_ret_status = STATUS_UNKNOWN_TRANSACTION;
-  std::shared_ptr<BnProtectedInterface> _aidl_impl = std::static_pointer_cast<BnProtectedInterface>(::ndk::ICInterface::asInterface(_aidl_binder));
-  switch (_aidl_code) {
-    case (FIRST_CALL_TRANSACTION + 0 /*Method1*/): {
-
-      ::ndk::ScopedAStatus _aidl_status = _aidl_impl->Method1();
-      _aidl_ret_status = AParcel_writeStatusHeader(_aidl_out, _aidl_status.get());
-      if (_aidl_ret_status != STATUS_OK) break;
-
-      if (!AStatus_isOk(_aidl_status.get())) break;
-
-      break;
-    }
-    case (FIRST_CALL_TRANSACTION + 1 /*Method2*/): {
-
-      ::ndk::ScopedAStatus _aidl_status = _aidl_impl->Method2();
-      _aidl_ret_status = AParcel_writeStatusHeader(_aidl_out, _aidl_status.get());
-      if (_aidl_ret_status != STATUS_OK) break;
-
-      if (!AStatus_isOk(_aidl_status.get())) break;
-
-      break;
-    }
-  }
-  return _aidl_ret_status;
-}
-
-static AIBinder_Class* _g_aidl_android_aidl_tests_permission_IProtectedInterface_clazz = ::ndk::ICInterface::defineClass(IProtectedInterface::descriptor, _aidl_android_aidl_tests_permission_IProtectedInterface_onTransact);
-
-BpProtectedInterface::BpProtectedInterface(const ::ndk::SpAIBinder& binder) : BpCInterface(binder) {}
-BpProtectedInterface::~BpProtectedInterface() {}
-
-::ndk::ScopedAStatus BpProtectedInterface::Method1() {
-  binder_status_t _aidl_ret_status = STATUS_OK;
-  ::ndk::ScopedAStatus _aidl_status;
-  ::ndk::ScopedAParcel _aidl_in;
-  ::ndk::ScopedAParcel _aidl_out;
-
-  _aidl_ret_status = AIBinder_prepareTransaction(asBinder().get(), _aidl_in.getR());
-  if (_aidl_ret_status != STATUS_OK) goto _aidl_error;
-
-  _aidl_ret_status = AIBinder_transact(
-    asBinder().get(),
-    (FIRST_CALL_TRANSACTION + 0 /*Method1*/),
-    _aidl_in.getR(),
-    _aidl_out.getR(),
-    0
-    #ifdef BINDER_STABILITY_SUPPORT
-    | FLAG_PRIVATE_LOCAL
-    #endif  // BINDER_STABILITY_SUPPORT
-    );
-  if (_aidl_ret_status == STATUS_UNKNOWN_TRANSACTION && IProtectedInterface::getDefaultImpl()) {
-    _aidl_status = IProtectedInterface::getDefaultImpl()->Method1();
-    goto _aidl_status_return;
-  }
-  if (_aidl_ret_status != STATUS_OK) goto _aidl_error;
-
-  _aidl_ret_status = AParcel_readStatusHeader(_aidl_out.get(), _aidl_status.getR());
-  if (_aidl_ret_status != STATUS_OK) goto _aidl_error;
-
-  if (!AStatus_isOk(_aidl_status.get())) goto _aidl_status_return;
-  _aidl_error:
-  _aidl_status.set(AStatus_fromStatus(_aidl_ret_status));
-  _aidl_status_return:
-  return _aidl_status;
-}
-::ndk::ScopedAStatus BpProtectedInterface::Method2() {
-  binder_status_t _aidl_ret_status = STATUS_OK;
-  ::ndk::ScopedAStatus _aidl_status;
-  ::ndk::ScopedAParcel _aidl_in;
-  ::ndk::ScopedAParcel _aidl_out;
-
-  _aidl_ret_status = AIBinder_prepareTransaction(asBinder().get(), _aidl_in.getR());
-  if (_aidl_ret_status != STATUS_OK) goto _aidl_error;
-
-  _aidl_ret_status = AIBinder_transact(
-    asBinder().get(),
-    (FIRST_CALL_TRANSACTION + 1 /*Method2*/),
-    _aidl_in.getR(),
-    _aidl_out.getR(),
-    0
-    #ifdef BINDER_STABILITY_SUPPORT
-    | FLAG_PRIVATE_LOCAL
-    #endif  // BINDER_STABILITY_SUPPORT
-    );
-  if (_aidl_ret_status == STATUS_UNKNOWN_TRANSACTION && IProtectedInterface::getDefaultImpl()) {
-    _aidl_status = IProtectedInterface::getDefaultImpl()->Method2();
-    goto _aidl_status_return;
-  }
-  if (_aidl_ret_status != STATUS_OK) goto _aidl_error;
-
-  _aidl_ret_status = AParcel_readStatusHeader(_aidl_out.get(), _aidl_status.getR());
-  if (_aidl_ret_status != STATUS_OK) goto _aidl_error;
-
-  if (!AStatus_isOk(_aidl_status.get())) goto _aidl_status_return;
-  _aidl_error:
-  _aidl_status.set(AStatus_fromStatus(_aidl_ret_status));
-  _aidl_status_return:
-  return _aidl_status;
-}
-// Source for BnProtectedInterface
-BnProtectedInterface::BnProtectedInterface() {}
-BnProtectedInterface::~BnProtectedInterface() {}
-::ndk::SpAIBinder BnProtectedInterface::createBinder() {
-  AIBinder* binder = AIBinder_new(_g_aidl_android_aidl_tests_permission_IProtectedInterface_clazz, static_cast<void*>(this));
-  #ifdef BINDER_STABILITY_SUPPORT
-  AIBinder_markCompilationUnitStability(binder);
-  #endif  // BINDER_STABILITY_SUPPORT
-  return ::ndk::SpAIBinder(binder);
-}
-// Source for IProtectedInterface
-const char* IProtectedInterface::descriptor = "android.aidl.tests.permission.IProtectedInterface";
-IProtectedInterface::IProtectedInterface() {}
-IProtectedInterface::~IProtectedInterface() {}
-
-
-std::shared_ptr<IProtectedInterface> IProtectedInterface::fromBinder(const ::ndk::SpAIBinder& binder) {
-  if (!AIBinder_associateClass(binder.get(), _g_aidl_android_aidl_tests_permission_IProtectedInterface_clazz)) { return nullptr; }
-  std::shared_ptr<::ndk::ICInterface> interface = ::ndk::ICInterface::asInterface(binder.get());
-  if (interface) {
-    return std::static_pointer_cast<IProtectedInterface>(interface);
-  }
-  return ::ndk::SharedRefBase::make<BpProtectedInterface>(binder);
-}
-
-binder_status_t IProtectedInterface::writeToParcel(AParcel* parcel, const std::shared_ptr<IProtectedInterface>& instance) {
-  return AParcel_writeStrongBinder(parcel, instance ? instance->asBinder().get() : nullptr);
-}
-binder_status_t IProtectedInterface::readFromParcel(const AParcel* parcel, std::shared_ptr<IProtectedInterface>* instance) {
-  ::ndk::SpAIBinder binder;
-  binder_status_t status = AParcel_readStrongBinder(parcel, binder.getR());
-  if (status != STATUS_OK) return status;
-  *instance = IProtectedInterface::fromBinder(binder);
-  return STATUS_OK;
-}
-bool IProtectedInterface::setDefaultImpl(const std::shared_ptr<IProtectedInterface>& impl) {
-  // Only one user of this interface can use this function
-  // at a time. This is a heuristic to detect if two different
-  // users in the same process use this function.
-  assert(!IProtectedInterface::default_impl);
-  if (impl) {
-    IProtectedInterface::default_impl = impl;
-    return true;
-  }
-  return false;
-}
-const std::shared_ptr<IProtectedInterface>& IProtectedInterface::getDefaultImpl() {
-  return IProtectedInterface::default_impl;
-}
-std::shared_ptr<IProtectedInterface> IProtectedInterface::default_impl = nullptr;
-::ndk::ScopedAStatus IProtectedInterfaceDefault::Method1() {
-  ::ndk::ScopedAStatus _aidl_status;
-  _aidl_status.set(AStatus_fromStatus(STATUS_UNKNOWN_TRANSACTION));
-  return _aidl_status;
-}
-::ndk::ScopedAStatus IProtectedInterfaceDefault::Method2() {
-  ::ndk::ScopedAStatus _aidl_status;
-  _aidl_status.set(AStatus_fromStatus(STATUS_UNKNOWN_TRANSACTION));
-  return _aidl_status;
-}
-::ndk::SpAIBinder IProtectedInterfaceDefault::asBinder() {
-  return ::ndk::SpAIBinder();
-}
-bool IProtectedInterfaceDefault::isRemote() {
-  return false;
-}
-}  // namespace permission
-}  // namespace tests
-}  // namespace aidl
-}  // namespace android
-}  // namespace aidl
diff --git a/tests/golden_output/aidl-test-interface-ndk-source/gen/android/aidl/tests/permission/IProtectedInterface.cpp.d b/tests/golden_output/aidl-test-interface-ndk-source/gen/android/aidl/tests/permission/IProtectedInterface.cpp.d
deleted file mode 100644
index 0dbab58..0000000
--- a/tests/golden_output/aidl-test-interface-ndk-source/gen/android/aidl/tests/permission/IProtectedInterface.cpp.d
+++ /dev/null
@@ -1,2 +0,0 @@
-out/soong/.intermediates/system/tools/aidl/aidl-test-interface-ndk-source/gen/android/aidl/tests/permission/IProtectedInterface.cpp : \
-  system/tools/aidl/tests/android/aidl/tests/permission/IProtectedInterface.aidl
diff --git a/tests/golden_output/aidl-test-interface-ndk-source/gen/android/aidl/tests/unions/EnumUnion.cpp b/tests/golden_output/aidl-test-interface-ndk-source/gen/android/aidl/tests/unions/EnumUnion.cpp
index 08bcd8c..c3f473f 100644
--- a/tests/golden_output/aidl-test-interface-ndk-source/gen/android/aidl/tests/unions/EnumUnion.cpp
+++ b/tests/golden_output/aidl-test-interface-ndk-source/gen/android/aidl/tests/unions/EnumUnion.cpp
@@ -13,7 +13,7 @@
   binder_status_t _aidl_ret_status;
   int32_t _aidl_tag;
   if ((_aidl_ret_status = ::ndk::AParcel_readData(_parcel, &_aidl_tag)) != STATUS_OK) return _aidl_ret_status;
-  switch (_aidl_tag) {
+  switch (static_cast<Tag>(_aidl_tag)) {
   case intEnum: {
     ::aidl::android::aidl::tests::IntEnum _aidl_value;
     if ((_aidl_ret_status = ::ndk::AParcel_readData(_parcel, &_aidl_value)) != STATUS_OK) return _aidl_ret_status;
@@ -34,15 +34,32 @@
       set<longEnum>(std::move(_aidl_value));
     }
     return STATUS_OK; }
+  #pragma clang diagnostic push
+  #pragma clang diagnostic ignored "-Wdeprecated-declarations"
+  case deprecatedField: {
+    int32_t _aidl_value;
+    if ((_aidl_ret_status = ::ndk::AParcel_readData(_parcel, &_aidl_value)) != STATUS_OK) return _aidl_ret_status;
+    if constexpr (std::is_trivially_copyable_v<int32_t>) {
+      set<deprecatedField>(_aidl_value);
+    } else {
+      // NOLINTNEXTLINE(performance-move-const-arg)
+      set<deprecatedField>(std::move(_aidl_value));
+    }
+    return STATUS_OK; }
+  #pragma clang diagnostic pop
   }
   return STATUS_BAD_VALUE;
 }
 binder_status_t EnumUnion::writeToParcel(AParcel* _parcel) const {
-  binder_status_t _aidl_ret_status = ::ndk::AParcel_writeData(_parcel, getTag());
+  binder_status_t _aidl_ret_status = ::ndk::AParcel_writeData(_parcel, static_cast<int32_t>(getTag()));
   if (_aidl_ret_status != STATUS_OK) return _aidl_ret_status;
   switch (getTag()) {
   case intEnum: return ::ndk::AParcel_writeData(_parcel, get<intEnum>());
   case longEnum: return ::ndk::AParcel_writeData(_parcel, get<longEnum>());
+  #pragma clang diagnostic push
+  #pragma clang diagnostic ignored "-Wdeprecated-declarations"
+  case deprecatedField: return ::ndk::AParcel_writeData(_parcel, get<deprecatedField>());
+  #pragma clang diagnostic pop
   }
   __assert2(__FILE__, __LINE__, __PRETTY_FUNCTION__, "can't reach here");
 }
diff --git a/tests/golden_output/aidl-test-interface-ndk-source/gen/android/aidl/tests/unions/UnionInUnion.cpp b/tests/golden_output/aidl-test-interface-ndk-source/gen/android/aidl/tests/unions/UnionInUnion.cpp
index 5db5c98..670eb34 100644
--- a/tests/golden_output/aidl-test-interface-ndk-source/gen/android/aidl/tests/unions/UnionInUnion.cpp
+++ b/tests/golden_output/aidl-test-interface-ndk-source/gen/android/aidl/tests/unions/UnionInUnion.cpp
@@ -13,7 +13,7 @@
   binder_status_t _aidl_ret_status;
   int32_t _aidl_tag;
   if ((_aidl_ret_status = ::ndk::AParcel_readData(_parcel, &_aidl_tag)) != STATUS_OK) return _aidl_ret_status;
-  switch (_aidl_tag) {
+  switch (static_cast<Tag>(_aidl_tag)) {
   case first: {
     ::aidl::android::aidl::tests::unions::EnumUnion _aidl_value;
     if ((_aidl_ret_status = ::ndk::AParcel_readData(_parcel, &_aidl_value)) != STATUS_OK) return _aidl_ret_status;
@@ -38,7 +38,7 @@
   return STATUS_BAD_VALUE;
 }
 binder_status_t UnionInUnion::writeToParcel(AParcel* _parcel) const {
-  binder_status_t _aidl_ret_status = ::ndk::AParcel_writeData(_parcel, getTag());
+  binder_status_t _aidl_ret_status = ::ndk::AParcel_writeData(_parcel, static_cast<int32_t>(getTag()));
   if (_aidl_ret_status != STATUS_OK) return _aidl_ret_status;
   switch (getTag()) {
   case first: return ::ndk::AParcel_writeData(_parcel, get<first>());
diff --git a/tests/golden_output/aidl-test-interface-ndk-source/gen/include/aidl/android/aidl/tests/ArrayOfInterfaces.h b/tests/golden_output/aidl-test-interface-ndk-source/gen/include/aidl/android/aidl/tests/ArrayOfInterfaces.h
index 6e43bf0..b2b9e7b 100644
--- a/tests/golden_output/aidl-test-interface-ndk-source/gen/include/aidl/android/aidl/tests/ArrayOfInterfaces.h
+++ b/tests/golden_output/aidl-test-interface-ndk-source/gen/include/aidl/android/aidl/tests/ArrayOfInterfaces.h
@@ -1,5 +1,6 @@
 #pragma once
 
+#include <array>
 #include <cassert>
 #include <cstdint>
 #include <memory>
@@ -9,6 +10,7 @@
 #include <utility>
 #include <variant>
 #include <vector>
+#include <android/binder_enums.h>
 #include <android/binder_ibinder.h>
 #include <android/binder_interface_utils.h>
 #include <android/binder_parcelable_utils.h>
@@ -152,17 +154,23 @@
     typedef std::false_type fixed_size;
     static const char* descriptor;
 
-    enum Tag : int32_t {
-      iface = 0,  // android.aidl.tests.ArrayOfInterfaces.IEmptyInterface iface;
-      nullable_iface,  // android.aidl.tests.ArrayOfInterfaces.IEmptyInterface nullable_iface;
-      iface_array,  // android.aidl.tests.ArrayOfInterfaces.IEmptyInterface[] iface_array;
-      nullable_iface_array,  // android.aidl.tests.ArrayOfInterfaces.IEmptyInterface[] nullable_iface_array;
+    enum class Tag : int32_t {
+      iface = 0,
+      nullable_iface = 1,
+      iface_array = 2,
+      nullable_iface_array = 3,
     };
 
+    // Expose tag symbols for legacy code
+    static const inline Tag iface = Tag::iface;
+    static const inline Tag nullable_iface = Tag::nullable_iface;
+    static const inline Tag iface_array = Tag::iface_array;
+    static const inline Tag nullable_iface_array = Tag::nullable_iface_array;
+
     template<typename _Tp>
     static constexpr bool _not_self = !std::is_same_v<std::remove_cv_t<std::remove_reference_t<_Tp>>, MyUnion>;
 
-    MyUnion() : _value(std::in_place_index<iface>, std::shared_ptr<::aidl::android::aidl::tests::ArrayOfInterfaces::IEmptyInterface>()) { }
+    MyUnion() : _value(std::in_place_index<static_cast<size_t>(iface)>, std::shared_ptr<::aidl::android::aidl::tests::ArrayOfInterfaces::IEmptyInterface>()) { }
 
     template <typename _Tp, typename = std::enable_if_t<_not_self<_Tp>>>
     // NOLINTNEXTLINE(google-explicit-constructor)
@@ -175,12 +183,12 @@
 
     template <Tag _tag, typename... _Tp>
     static MyUnion make(_Tp&&... _args) {
-      return MyUnion(std::in_place_index<_tag>, std::forward<_Tp>(_args)...);
+      return MyUnion(std::in_place_index<static_cast<size_t>(_tag)>, std::forward<_Tp>(_args)...);
     }
 
     template <Tag _tag, typename _Tp, typename... _Up>
     static MyUnion make(std::initializer_list<_Tp> _il, _Up&&... _args) {
-      return MyUnion(std::in_place_index<_tag>, std::move(_il), std::forward<_Up>(_args)...);
+      return MyUnion(std::in_place_index<static_cast<size_t>(_tag)>, std::move(_il), std::forward<_Up>(_args)...);
     }
 
     Tag getTag() const {
@@ -190,18 +198,18 @@
     template <Tag _tag>
     const auto& get() const {
       if (getTag() != _tag) { __assert2(__FILE__, __LINE__, __PRETTY_FUNCTION__, "bad access: a wrong tag"); }
-      return std::get<_tag>(_value);
+      return std::get<static_cast<size_t>(_tag)>(_value);
     }
 
     template <Tag _tag>
     auto& get() {
       if (getTag() != _tag) { __assert2(__FILE__, __LINE__, __PRETTY_FUNCTION__, "bad access: a wrong tag"); }
-      return std::get<_tag>(_value);
+      return std::get<static_cast<size_t>(_tag)>(_value);
     }
 
     template <Tag _tag, typename... _Tp>
     void set(_Tp&&... _args) {
-      _value.emplace<_tag>(std::forward<_Tp>(_args)...);
+      _value.emplace<static_cast<size_t>(_tag)>(std::forward<_Tp>(_args)...);
     }
 
     binder_status_t readFromParcel(const AParcel* _parcel);
@@ -277,3 +285,39 @@
 }  // namespace aidl
 }  // namespace android
 }  // namespace aidl
+namespace aidl {
+namespace android {
+namespace aidl {
+namespace tests {
+[[nodiscard]] static inline std::string toString(ArrayOfInterfaces::MyUnion::Tag val) {
+  switch(val) {
+  case ArrayOfInterfaces::MyUnion::Tag::iface:
+    return "iface";
+  case ArrayOfInterfaces::MyUnion::Tag::nullable_iface:
+    return "nullable_iface";
+  case ArrayOfInterfaces::MyUnion::Tag::iface_array:
+    return "iface_array";
+  case ArrayOfInterfaces::MyUnion::Tag::nullable_iface_array:
+    return "nullable_iface_array";
+  default:
+    return std::to_string(static_cast<int32_t>(val));
+  }
+}
+}  // namespace tests
+}  // namespace aidl
+}  // namespace android
+}  // namespace aidl
+namespace ndk {
+namespace internal {
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wc++17-extensions"
+template <>
+constexpr inline std::array<aidl::android::aidl::tests::ArrayOfInterfaces::MyUnion::Tag, 4> enum_values<aidl::android::aidl::tests::ArrayOfInterfaces::MyUnion::Tag> = {
+  aidl::android::aidl::tests::ArrayOfInterfaces::MyUnion::Tag::iface,
+  aidl::android::aidl::tests::ArrayOfInterfaces::MyUnion::Tag::nullable_iface,
+  aidl::android::aidl::tests::ArrayOfInterfaces::MyUnion::Tag::iface_array,
+  aidl::android::aidl::tests::ArrayOfInterfaces::MyUnion::Tag::nullable_iface_array,
+};
+#pragma clang diagnostic pop
+}  // namespace internal
+}  // namespace ndk
diff --git a/tests/golden_output/aidl-test-interface-ndk-source/gen/include/aidl/android/aidl/tests/BnDeprecated.h b/tests/golden_output/aidl-test-interface-ndk-source/gen/include/aidl/android/aidl/tests/BnDeprecated.h
index 0b6f042..356e9a4 100644
--- a/tests/golden_output/aidl-test-interface-ndk-source/gen/include/aidl/android/aidl/tests/BnDeprecated.h
+++ b/tests/golden_output/aidl-test-interface-ndk-source/gen/include/aidl/android/aidl/tests/BnDeprecated.h
@@ -3,6 +3,13 @@
 #include "aidl/android/aidl/tests/IDeprecated.h"
 
 #include <android/binder_ibinder.h>
+#include <cassert>
+
+#ifndef __BIONIC__
+#ifndef __assert2
+#define __assert2(a,b,c,d) ((void)0)
+#endif
+#endif
 
 namespace aidl {
 namespace android {
@@ -16,6 +23,16 @@
   ::ndk::SpAIBinder createBinder() override;
 private:
 };
+class __attribute__((deprecated("test"))) IDeprecatedDelegator : public BnDeprecated {
+public:
+  explicit IDeprecatedDelegator(const std::shared_ptr<IDeprecated> &impl) : _impl(impl) {
+  }
+
+protected:
+private:
+  std::shared_ptr<IDeprecated> _impl;
+};
+
 }  // namespace tests
 }  // namespace aidl
 }  // namespace android
diff --git a/tests/golden_output/aidl-test-interface-ndk-source/gen/include/aidl/android/aidl/tests/BnNamedCallback.h b/tests/golden_output/aidl-test-interface-ndk-source/gen/include/aidl/android/aidl/tests/BnNamedCallback.h
index 6f38a28..fa465fa 100644
--- a/tests/golden_output/aidl-test-interface-ndk-source/gen/include/aidl/android/aidl/tests/BnNamedCallback.h
+++ b/tests/golden_output/aidl-test-interface-ndk-source/gen/include/aidl/android/aidl/tests/BnNamedCallback.h
@@ -3,6 +3,13 @@
 #include "aidl/android/aidl/tests/INamedCallback.h"
 
 #include <android/binder_ibinder.h>
+#include <cassert>
+
+#ifndef __BIONIC__
+#ifndef __assert2
+#define __assert2(a,b,c,d) ((void)0)
+#endif
+#endif
 
 namespace aidl {
 namespace android {
@@ -16,6 +23,19 @@
   ::ndk::SpAIBinder createBinder() override;
 private:
 };
+class INamedCallbackDelegator : public BnNamedCallback {
+public:
+  explicit INamedCallbackDelegator(const std::shared_ptr<INamedCallback> &impl) : _impl(impl) {
+  }
+
+  ::ndk::ScopedAStatus GetName(std::string* _aidl_return) override {
+    return _impl->GetName(_aidl_return);
+  }
+protected:
+private:
+  std::shared_ptr<INamedCallback> _impl;
+};
+
 }  // namespace tests
 }  // namespace aidl
 }  // namespace android
diff --git a/tests/golden_output/aidl-test-interface-ndk-source/gen/include/aidl/android/aidl/tests/BnNewName.h b/tests/golden_output/aidl-test-interface-ndk-source/gen/include/aidl/android/aidl/tests/BnNewName.h
index 32d8a7b..78fcbaa 100644
--- a/tests/golden_output/aidl-test-interface-ndk-source/gen/include/aidl/android/aidl/tests/BnNewName.h
+++ b/tests/golden_output/aidl-test-interface-ndk-source/gen/include/aidl/android/aidl/tests/BnNewName.h
@@ -3,6 +3,13 @@
 #include "aidl/android/aidl/tests/INewName.h"
 
 #include <android/binder_ibinder.h>
+#include <cassert>
+
+#ifndef __BIONIC__
+#ifndef __assert2
+#define __assert2(a,b,c,d) ((void)0)
+#endif
+#endif
 
 namespace aidl {
 namespace android {
@@ -16,6 +23,19 @@
   ::ndk::SpAIBinder createBinder() override;
 private:
 };
+class INewNameDelegator : public BnNewName {
+public:
+  explicit INewNameDelegator(const std::shared_ptr<INewName> &impl) : _impl(impl) {
+  }
+
+  ::ndk::ScopedAStatus RealName(std::string* _aidl_return) override {
+    return _impl->RealName(_aidl_return);
+  }
+protected:
+private:
+  std::shared_ptr<INewName> _impl;
+};
+
 }  // namespace tests
 }  // namespace aidl
 }  // namespace android
diff --git a/tests/golden_output/aidl-test-interface-ndk-source/gen/include/aidl/android/aidl/tests/BnOldName.h b/tests/golden_output/aidl-test-interface-ndk-source/gen/include/aidl/android/aidl/tests/BnOldName.h
index e1782d1..e08fcc1 100644
--- a/tests/golden_output/aidl-test-interface-ndk-source/gen/include/aidl/android/aidl/tests/BnOldName.h
+++ b/tests/golden_output/aidl-test-interface-ndk-source/gen/include/aidl/android/aidl/tests/BnOldName.h
@@ -3,6 +3,13 @@
 #include "aidl/android/aidl/tests/IOldName.h"
 
 #include <android/binder_ibinder.h>
+#include <cassert>
+
+#ifndef __BIONIC__
+#ifndef __assert2
+#define __assert2(a,b,c,d) ((void)0)
+#endif
+#endif
 
 namespace aidl {
 namespace android {
@@ -16,6 +23,19 @@
   ::ndk::SpAIBinder createBinder() override;
 private:
 };
+class IOldNameDelegator : public BnOldName {
+public:
+  explicit IOldNameDelegator(const std::shared_ptr<IOldName> &impl) : _impl(impl) {
+  }
+
+  ::ndk::ScopedAStatus RealName(std::string* _aidl_return) override {
+    return _impl->RealName(_aidl_return);
+  }
+protected:
+private:
+  std::shared_ptr<IOldName> _impl;
+};
+
 }  // namespace tests
 }  // namespace aidl
 }  // namespace android
diff --git a/tests/golden_output/aidl-test-interface-ndk-source/gen/include/aidl/android/aidl/tests/BnTestService.h b/tests/golden_output/aidl-test-interface-ndk-source/gen/include/aidl/android/aidl/tests/BnTestService.h
index a33592b..3b5a37d 100644
--- a/tests/golden_output/aidl-test-interface-ndk-source/gen/include/aidl/android/aidl/tests/BnTestService.h
+++ b/tests/golden_output/aidl-test-interface-ndk-source/gen/include/aidl/android/aidl/tests/BnTestService.h
@@ -3,6 +3,13 @@
 #include "aidl/android/aidl/tests/ITestService.h"
 
 #include <android/binder_ibinder.h>
+#include <cassert>
+
+#ifndef __BIONIC__
+#ifndef __assert2
+#define __assert2(a,b,c,d) ((void)0)
+#endif
+#endif
 
 namespace aidl {
 namespace android {
@@ -16,6 +23,214 @@
   ::ndk::SpAIBinder createBinder() override;
 private:
 };
+class ITestServiceDelegator : public BnTestService {
+public:
+  explicit ITestServiceDelegator(const std::shared_ptr<ITestService> &impl) : _impl(impl) {
+  }
+
+  ::ndk::ScopedAStatus UnimplementedMethod(int32_t in_arg, int32_t* _aidl_return) override {
+    return _impl->UnimplementedMethod(in_arg, _aidl_return);
+  }
+  ::ndk::ScopedAStatus Deprecated() override __attribute__((deprecated("to make sure we have something in system/tools/aidl which does a compile check of deprecated and make sure this is reflected in goldens"))) {
+    return _impl->Deprecated();
+  }
+  ::ndk::ScopedAStatus TestOneway() override {
+    return _impl->TestOneway();
+  }
+  ::ndk::ScopedAStatus RepeatBoolean(bool in_token, bool* _aidl_return) override {
+    return _impl->RepeatBoolean(in_token, _aidl_return);
+  }
+  ::ndk::ScopedAStatus RepeatByte(int8_t in_token, int8_t* _aidl_return) override {
+    return _impl->RepeatByte(in_token, _aidl_return);
+  }
+  ::ndk::ScopedAStatus RepeatChar(char16_t in_token, char16_t* _aidl_return) override {
+    return _impl->RepeatChar(in_token, _aidl_return);
+  }
+  ::ndk::ScopedAStatus RepeatInt(int32_t in_token, int32_t* _aidl_return) override {
+    return _impl->RepeatInt(in_token, _aidl_return);
+  }
+  ::ndk::ScopedAStatus RepeatLong(int64_t in_token, int64_t* _aidl_return) override {
+    return _impl->RepeatLong(in_token, _aidl_return);
+  }
+  ::ndk::ScopedAStatus RepeatFloat(float in_token, float* _aidl_return) override {
+    return _impl->RepeatFloat(in_token, _aidl_return);
+  }
+  ::ndk::ScopedAStatus RepeatDouble(double in_token, double* _aidl_return) override {
+    return _impl->RepeatDouble(in_token, _aidl_return);
+  }
+  ::ndk::ScopedAStatus RepeatString(const std::string& in_token, std::string* _aidl_return) override {
+    return _impl->RepeatString(in_token, _aidl_return);
+  }
+  ::ndk::ScopedAStatus RepeatByteEnum(::aidl::android::aidl::tests::ByteEnum in_token, ::aidl::android::aidl::tests::ByteEnum* _aidl_return) override {
+    return _impl->RepeatByteEnum(in_token, _aidl_return);
+  }
+  ::ndk::ScopedAStatus RepeatIntEnum(::aidl::android::aidl::tests::IntEnum in_token, ::aidl::android::aidl::tests::IntEnum* _aidl_return) override {
+    return _impl->RepeatIntEnum(in_token, _aidl_return);
+  }
+  ::ndk::ScopedAStatus RepeatLongEnum(::aidl::android::aidl::tests::LongEnum in_token, ::aidl::android::aidl::tests::LongEnum* _aidl_return) override {
+    return _impl->RepeatLongEnum(in_token, _aidl_return);
+  }
+  ::ndk::ScopedAStatus ReverseBoolean(const std::vector<bool>& in_input, std::vector<bool>* out_repeated, std::vector<bool>* _aidl_return) override {
+    return _impl->ReverseBoolean(in_input, out_repeated, _aidl_return);
+  }
+  ::ndk::ScopedAStatus ReverseByte(const std::vector<uint8_t>& in_input, std::vector<uint8_t>* out_repeated, std::vector<uint8_t>* _aidl_return) override {
+    return _impl->ReverseByte(in_input, out_repeated, _aidl_return);
+  }
+  ::ndk::ScopedAStatus ReverseChar(const std::vector<char16_t>& in_input, std::vector<char16_t>* out_repeated, std::vector<char16_t>* _aidl_return) override {
+    return _impl->ReverseChar(in_input, out_repeated, _aidl_return);
+  }
+  ::ndk::ScopedAStatus ReverseInt(const std::vector<int32_t>& in_input, std::vector<int32_t>* out_repeated, std::vector<int32_t>* _aidl_return) override {
+    return _impl->ReverseInt(in_input, out_repeated, _aidl_return);
+  }
+  ::ndk::ScopedAStatus ReverseLong(const std::vector<int64_t>& in_input, std::vector<int64_t>* out_repeated, std::vector<int64_t>* _aidl_return) override {
+    return _impl->ReverseLong(in_input, out_repeated, _aidl_return);
+  }
+  ::ndk::ScopedAStatus ReverseFloat(const std::vector<float>& in_input, std::vector<float>* out_repeated, std::vector<float>* _aidl_return) override {
+    return _impl->ReverseFloat(in_input, out_repeated, _aidl_return);
+  }
+  ::ndk::ScopedAStatus ReverseDouble(const std::vector<double>& in_input, std::vector<double>* out_repeated, std::vector<double>* _aidl_return) override {
+    return _impl->ReverseDouble(in_input, out_repeated, _aidl_return);
+  }
+  ::ndk::ScopedAStatus ReverseString(const std::vector<std::string>& in_input, std::vector<std::string>* out_repeated, std::vector<std::string>* _aidl_return) override {
+    return _impl->ReverseString(in_input, out_repeated, _aidl_return);
+  }
+  ::ndk::ScopedAStatus ReverseByteEnum(const std::vector<::aidl::android::aidl::tests::ByteEnum>& in_input, std::vector<::aidl::android::aidl::tests::ByteEnum>* out_repeated, std::vector<::aidl::android::aidl::tests::ByteEnum>* _aidl_return) override {
+    return _impl->ReverseByteEnum(in_input, out_repeated, _aidl_return);
+  }
+  ::ndk::ScopedAStatus ReverseIntEnum(const std::vector<::aidl::android::aidl::tests::IntEnum>& in_input, std::vector<::aidl::android::aidl::tests::IntEnum>* out_repeated, std::vector<::aidl::android::aidl::tests::IntEnum>* _aidl_return) override {
+    return _impl->ReverseIntEnum(in_input, out_repeated, _aidl_return);
+  }
+  ::ndk::ScopedAStatus ReverseLongEnum(const std::vector<::aidl::android::aidl::tests::LongEnum>& in_input, std::vector<::aidl::android::aidl::tests::LongEnum>* out_repeated, std::vector<::aidl::android::aidl::tests::LongEnum>* _aidl_return) override {
+    return _impl->ReverseLongEnum(in_input, out_repeated, _aidl_return);
+  }
+  ::ndk::ScopedAStatus GetOtherTestService(const std::string& in_name, std::shared_ptr<::aidl::android::aidl::tests::INamedCallback>* _aidl_return) override {
+    return _impl->GetOtherTestService(in_name, _aidl_return);
+  }
+  ::ndk::ScopedAStatus VerifyName(const std::shared_ptr<::aidl::android::aidl::tests::INamedCallback>& in_service, const std::string& in_name, bool* _aidl_return) override {
+    return _impl->VerifyName(in_service, in_name, _aidl_return);
+  }
+  ::ndk::ScopedAStatus GetInterfaceArray(const std::vector<std::string>& in_names, std::vector<std::shared_ptr<::aidl::android::aidl::tests::INamedCallback>>* _aidl_return) override {
+    return _impl->GetInterfaceArray(in_names, _aidl_return);
+  }
+  ::ndk::ScopedAStatus VerifyNamesWithInterfaceArray(const std::vector<std::shared_ptr<::aidl::android::aidl::tests::INamedCallback>>& in_services, const std::vector<std::string>& in_names, bool* _aidl_return) override {
+    return _impl->VerifyNamesWithInterfaceArray(in_services, in_names, _aidl_return);
+  }
+  ::ndk::ScopedAStatus GetNullableInterfaceArray(const std::optional<std::vector<std::optional<std::string>>>& in_names, std::optional<std::vector<std::shared_ptr<::aidl::android::aidl::tests::INamedCallback>>>* _aidl_return) override {
+    return _impl->GetNullableInterfaceArray(in_names, _aidl_return);
+  }
+  ::ndk::ScopedAStatus VerifyNamesWithNullableInterfaceArray(const std::optional<std::vector<std::shared_ptr<::aidl::android::aidl::tests::INamedCallback>>>& in_services, const std::optional<std::vector<std::optional<std::string>>>& in_names, bool* _aidl_return) override {
+    return _impl->VerifyNamesWithNullableInterfaceArray(in_services, in_names, _aidl_return);
+  }
+  ::ndk::ScopedAStatus GetInterfaceList(const std::optional<std::vector<std::optional<std::string>>>& in_names, std::optional<std::vector<std::shared_ptr<::aidl::android::aidl::tests::INamedCallback>>>* _aidl_return) override {
+    return _impl->GetInterfaceList(in_names, _aidl_return);
+  }
+  ::ndk::ScopedAStatus VerifyNamesWithInterfaceList(const std::optional<std::vector<std::shared_ptr<::aidl::android::aidl::tests::INamedCallback>>>& in_services, const std::optional<std::vector<std::optional<std::string>>>& in_names, bool* _aidl_return) override {
+    return _impl->VerifyNamesWithInterfaceList(in_services, in_names, _aidl_return);
+  }
+  ::ndk::ScopedAStatus ReverseStringList(const std::vector<std::string>& in_input, std::vector<std::string>* out_repeated, std::vector<std::string>* _aidl_return) override {
+    return _impl->ReverseStringList(in_input, out_repeated, _aidl_return);
+  }
+  ::ndk::ScopedAStatus RepeatParcelFileDescriptor(const ::ndk::ScopedFileDescriptor& in_read, ::ndk::ScopedFileDescriptor* _aidl_return) override {
+    return _impl->RepeatParcelFileDescriptor(in_read, _aidl_return);
+  }
+  ::ndk::ScopedAStatus ReverseParcelFileDescriptorArray(const std::vector<::ndk::ScopedFileDescriptor>& in_input, std::vector<::ndk::ScopedFileDescriptor>* out_repeated, std::vector<::ndk::ScopedFileDescriptor>* _aidl_return) override {
+    return _impl->ReverseParcelFileDescriptorArray(in_input, out_repeated, _aidl_return);
+  }
+  ::ndk::ScopedAStatus ThrowServiceException(int32_t in_code) override {
+    return _impl->ThrowServiceException(in_code);
+  }
+  ::ndk::ScopedAStatus RepeatNullableIntArray(const std::optional<std::vector<int32_t>>& in_input, std::optional<std::vector<int32_t>>* _aidl_return) override {
+    return _impl->RepeatNullableIntArray(in_input, _aidl_return);
+  }
+  ::ndk::ScopedAStatus RepeatNullableByteEnumArray(const std::optional<std::vector<::aidl::android::aidl::tests::ByteEnum>>& in_input, std::optional<std::vector<::aidl::android::aidl::tests::ByteEnum>>* _aidl_return) override {
+    return _impl->RepeatNullableByteEnumArray(in_input, _aidl_return);
+  }
+  ::ndk::ScopedAStatus RepeatNullableIntEnumArray(const std::optional<std::vector<::aidl::android::aidl::tests::IntEnum>>& in_input, std::optional<std::vector<::aidl::android::aidl::tests::IntEnum>>* _aidl_return) override {
+    return _impl->RepeatNullableIntEnumArray(in_input, _aidl_return);
+  }
+  ::ndk::ScopedAStatus RepeatNullableLongEnumArray(const std::optional<std::vector<::aidl::android::aidl::tests::LongEnum>>& in_input, std::optional<std::vector<::aidl::android::aidl::tests::LongEnum>>* _aidl_return) override {
+    return _impl->RepeatNullableLongEnumArray(in_input, _aidl_return);
+  }
+  ::ndk::ScopedAStatus RepeatNullableString(const std::optional<std::string>& in_input, std::optional<std::string>* _aidl_return) override {
+    return _impl->RepeatNullableString(in_input, _aidl_return);
+  }
+  ::ndk::ScopedAStatus RepeatNullableStringList(const std::optional<std::vector<std::optional<std::string>>>& in_input, std::optional<std::vector<std::optional<std::string>>>* _aidl_return) override {
+    return _impl->RepeatNullableStringList(in_input, _aidl_return);
+  }
+  ::ndk::ScopedAStatus RepeatNullableParcelable(const std::optional<::aidl::android::aidl::tests::ITestService::Empty>& in_input, std::optional<::aidl::android::aidl::tests::ITestService::Empty>* _aidl_return) override {
+    return _impl->RepeatNullableParcelable(in_input, _aidl_return);
+  }
+  ::ndk::ScopedAStatus RepeatNullableParcelableArray(const std::optional<std::vector<std::optional<::aidl::android::aidl::tests::ITestService::Empty>>>& in_input, std::optional<std::vector<std::optional<::aidl::android::aidl::tests::ITestService::Empty>>>* _aidl_return) override {
+    return _impl->RepeatNullableParcelableArray(in_input, _aidl_return);
+  }
+  ::ndk::ScopedAStatus RepeatNullableParcelableList(const std::optional<std::vector<std::optional<::aidl::android::aidl::tests::ITestService::Empty>>>& in_input, std::optional<std::vector<std::optional<::aidl::android::aidl::tests::ITestService::Empty>>>* _aidl_return) override {
+    return _impl->RepeatNullableParcelableList(in_input, _aidl_return);
+  }
+  ::ndk::ScopedAStatus TakesAnIBinder(const ::ndk::SpAIBinder& in_input) override {
+    return _impl->TakesAnIBinder(in_input);
+  }
+  ::ndk::ScopedAStatus TakesANullableIBinder(const ::ndk::SpAIBinder& in_input) override {
+    return _impl->TakesANullableIBinder(in_input);
+  }
+  ::ndk::ScopedAStatus TakesAnIBinderList(const std::vector<::ndk::SpAIBinder>& in_input) override {
+    return _impl->TakesAnIBinderList(in_input);
+  }
+  ::ndk::ScopedAStatus TakesANullableIBinderList(const std::optional<std::vector<::ndk::SpAIBinder>>& in_input) override {
+    return _impl->TakesANullableIBinderList(in_input);
+  }
+  ::ndk::ScopedAStatus RepeatUtf8CppString(const std::string& in_token, std::string* _aidl_return) override {
+    return _impl->RepeatUtf8CppString(in_token, _aidl_return);
+  }
+  ::ndk::ScopedAStatus RepeatNullableUtf8CppString(const std::optional<std::string>& in_token, std::optional<std::string>* _aidl_return) override {
+    return _impl->RepeatNullableUtf8CppString(in_token, _aidl_return);
+  }
+  ::ndk::ScopedAStatus ReverseUtf8CppString(const std::vector<std::string>& in_input, std::vector<std::string>* out_repeated, std::vector<std::string>* _aidl_return) override {
+    return _impl->ReverseUtf8CppString(in_input, out_repeated, _aidl_return);
+  }
+  ::ndk::ScopedAStatus ReverseNullableUtf8CppString(const std::optional<std::vector<std::optional<std::string>>>& in_input, std::optional<std::vector<std::optional<std::string>>>* out_repeated, std::optional<std::vector<std::optional<std::string>>>* _aidl_return) override {
+    return _impl->ReverseNullableUtf8CppString(in_input, out_repeated, _aidl_return);
+  }
+  ::ndk::ScopedAStatus ReverseUtf8CppStringList(const std::optional<std::vector<std::optional<std::string>>>& in_input, std::optional<std::vector<std::optional<std::string>>>* out_repeated, std::optional<std::vector<std::optional<std::string>>>* _aidl_return) override {
+    return _impl->ReverseUtf8CppStringList(in_input, out_repeated, _aidl_return);
+  }
+  ::ndk::ScopedAStatus GetCallback(bool in_return_null, std::shared_ptr<::aidl::android::aidl::tests::INamedCallback>* _aidl_return) override {
+    return _impl->GetCallback(in_return_null, _aidl_return);
+  }
+  ::ndk::ScopedAStatus FillOutStructuredParcelable(::aidl::android::aidl::tests::StructuredParcelable* in_parcel) override {
+    return _impl->FillOutStructuredParcelable(in_parcel);
+  }
+  ::ndk::ScopedAStatus RepeatExtendableParcelable(const ::aidl::android::aidl::tests::extension::ExtendableParcelable& in_ep, ::aidl::android::aidl::tests::extension::ExtendableParcelable* out_ep2) override {
+    return _impl->RepeatExtendableParcelable(in_ep, out_ep2);
+  }
+  ::ndk::ScopedAStatus ReverseList(const ::aidl::android::aidl::tests::RecursiveList& in_list, ::aidl::android::aidl::tests::RecursiveList* _aidl_return) override {
+    return _impl->ReverseList(in_list, _aidl_return);
+  }
+  ::ndk::ScopedAStatus ReverseIBinderArray(const std::vector<::ndk::SpAIBinder>& in_input, std::vector<::ndk::SpAIBinder>* out_repeated, std::vector<::ndk::SpAIBinder>* _aidl_return) override {
+    return _impl->ReverseIBinderArray(in_input, out_repeated, _aidl_return);
+  }
+  ::ndk::ScopedAStatus ReverseNullableIBinderArray(const std::optional<std::vector<::ndk::SpAIBinder>>& in_input, std::optional<std::vector<::ndk::SpAIBinder>>* out_repeated, std::optional<std::vector<::ndk::SpAIBinder>>* _aidl_return) override {
+    return _impl->ReverseNullableIBinderArray(in_input, out_repeated, _aidl_return);
+  }
+  ::ndk::ScopedAStatus GetOldNameInterface(std::shared_ptr<::aidl::android::aidl::tests::IOldName>* _aidl_return) override {
+    return _impl->GetOldNameInterface(_aidl_return);
+  }
+  ::ndk::ScopedAStatus GetNewNameInterface(std::shared_ptr<::aidl::android::aidl::tests::INewName>* _aidl_return) override {
+    return _impl->GetNewNameInterface(_aidl_return);
+  }
+  ::ndk::ScopedAStatus GetUnionTags(const std::vector<::aidl::android::aidl::tests::Union>& in_input, std::vector<::aidl::android::aidl::tests::Union::Tag>* _aidl_return) override {
+    return _impl->GetUnionTags(in_input, _aidl_return);
+  }
+  ::ndk::ScopedAStatus GetCppJavaTests(::ndk::SpAIBinder* _aidl_return) override {
+    return _impl->GetCppJavaTests(_aidl_return);
+  }
+  ::ndk::ScopedAStatus getBackendType(::aidl::android::aidl::tests::BackendType* _aidl_return) override {
+    return _impl->getBackendType(_aidl_return);
+  }
+protected:
+private:
+  std::shared_ptr<ITestService> _impl;
+};
+
 }  // namespace tests
 }  // namespace aidl
 }  // namespace android
diff --git a/tests/golden_output/aidl-test-interface-ndk-source/gen/include/aidl/android/aidl/tests/BpTestService.h b/tests/golden_output/aidl-test-interface-ndk-source/gen/include/aidl/android/aidl/tests/BpTestService.h
index 71b75be..2cd9768 100644
--- a/tests/golden_output/aidl-test-interface-ndk-source/gen/include/aidl/android/aidl/tests/BpTestService.h
+++ b/tests/golden_output/aidl-test-interface-ndk-source/gen/include/aidl/android/aidl/tests/BpTestService.h
@@ -76,6 +76,7 @@
   ::ndk::ScopedAStatus ReverseNullableIBinderArray(const std::optional<std::vector<::ndk::SpAIBinder>>& in_input, std::optional<std::vector<::ndk::SpAIBinder>>* out_repeated, std::optional<std::vector<::ndk::SpAIBinder>>* _aidl_return) override;
   ::ndk::ScopedAStatus GetOldNameInterface(std::shared_ptr<::aidl::android::aidl::tests::IOldName>* _aidl_return) override;
   ::ndk::ScopedAStatus GetNewNameInterface(std::shared_ptr<::aidl::android::aidl::tests::INewName>* _aidl_return) override;
+  ::ndk::ScopedAStatus GetUnionTags(const std::vector<::aidl::android::aidl::tests::Union>& in_input, std::vector<::aidl::android::aidl::tests::Union::Tag>* _aidl_return) override;
   ::ndk::ScopedAStatus GetCppJavaTests(::ndk::SpAIBinder* _aidl_return) override;
   ::ndk::ScopedAStatus getBackendType(::aidl::android::aidl::tests::BackendType* _aidl_return) override;
 };
diff --git a/tests/golden_output/aidl-test-interface-ndk-source/gen/include/aidl/android/aidl/tests/DeprecatedEnum.h b/tests/golden_output/aidl-test-interface-ndk-source/gen/include/aidl/android/aidl/tests/DeprecatedEnum.h
index 299874c..fce736b 100644
--- a/tests/golden_output/aidl-test-interface-ndk-source/gen/include/aidl/android/aidl/tests/DeprecatedEnum.h
+++ b/tests/golden_output/aidl-test-interface-ndk-source/gen/include/aidl/android/aidl/tests/DeprecatedEnum.h
@@ -29,7 +29,8 @@
 namespace android {
 namespace aidl {
 namespace tests {
-[[nodiscard]] static inline std::string toString(DeprecatedEnum val) __attribute__((deprecated("test")));
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wdeprecated-declarations"
 [[nodiscard]] static inline std::string toString(DeprecatedEnum val) {
   switch(val) {
   case DeprecatedEnum::A:
@@ -42,6 +43,7 @@
     return std::to_string(static_cast<int32_t>(val));
   }
 }
+#pragma clang diagnostic pop
 }  // namespace tests
 }  // namespace aidl
 }  // namespace android
@@ -50,8 +52,9 @@
 namespace internal {
 #pragma clang diagnostic push
 #pragma clang diagnostic ignored "-Wc++17-extensions"
+#pragma clang diagnostic ignored "-Wdeprecated-declarations"
 template <>
-constexpr inline std::array<aidl::android::aidl::tests::DeprecatedEnum, 3> __attribute__((deprecated("test"))) enum_values<aidl::android::aidl::tests::DeprecatedEnum> = {
+constexpr inline std::array<aidl::android::aidl::tests::DeprecatedEnum, 3> enum_values<aidl::android::aidl::tests::DeprecatedEnum> = {
   aidl::android::aidl::tests::DeprecatedEnum::A,
   aidl::android::aidl::tests::DeprecatedEnum::B,
   aidl::android::aidl::tests::DeprecatedEnum::C,
diff --git a/tests/golden_output/aidl-test-interface-ndk-source/gen/include/aidl/android/aidl/tests/FixedSize.h b/tests/golden_output/aidl-test-interface-ndk-source/gen/include/aidl/android/aidl/tests/FixedSize.h
index 2a78374..541757a 100644
--- a/tests/golden_output/aidl-test-interface-ndk-source/gen/include/aidl/android/aidl/tests/FixedSize.h
+++ b/tests/golden_output/aidl-test-interface-ndk-source/gen/include/aidl/android/aidl/tests/FixedSize.h
@@ -1,5 +1,6 @@
 #pragma once
 
+#include <array>
 #include <cassert>
 #include <cstdint>
 #include <memory>
@@ -10,6 +11,7 @@
 #include <utility>
 #include <variant>
 #include <vector>
+#include <android/binder_enums.h>
 #include <android/binder_interface_utils.h>
 #include <android/binder_parcelable_utils.h>
 #include <android/binder_to_string.h>
@@ -37,19 +39,29 @@
     typedef std::true_type fixed_size;
     static const char* descriptor;
 
-    enum Tag : uint8_t {
-      booleanValue = 0,  // boolean booleanValue;
-      byteValue,  // byte byteValue;
-      charValue,  // char charValue;
-      intValue,  // int intValue;
-      longValue,  // long longValue;
-      floatValue,  // float floatValue;
-      doubleValue,  // double doubleValue;
-      enumValue,  // android.aidl.tests.LongEnum enumValue;
+    enum class Tag : int8_t {
+      booleanValue = 0,
+      byteValue = 1,
+      charValue = 2,
+      intValue = 3,
+      longValue = 4,
+      floatValue = 5,
+      doubleValue = 6,
+      enumValue = 7,
     };
 
+    // Expose tag symbols for legacy code
+    static const inline Tag booleanValue = Tag::booleanValue;
+    static const inline Tag byteValue = Tag::byteValue;
+    static const inline Tag charValue = Tag::charValue;
+    static const inline Tag intValue = Tag::intValue;
+    static const inline Tag longValue = Tag::longValue;
+    static const inline Tag floatValue = Tag::floatValue;
+    static const inline Tag doubleValue = Tag::doubleValue;
+    static const inline Tag enumValue = Tag::enumValue;
+
     template <Tag _Tag>
-    using _at = typename std::tuple_element<_Tag, std::tuple<bool, int8_t, char16_t, int32_t, int64_t, float, double, ::aidl::android::aidl::tests::LongEnum>>::type;
+    using _at = typename std::tuple_element<static_cast<size_t>(_Tag), std::tuple<bool, int8_t, char16_t, int32_t, int64_t, float, double, ::aidl::android::aidl::tests::LongEnum>>::type;
     template <Tag _Tag, typename _Type>
     static FixedUnion make(_Type&& _arg) {
       FixedUnion _inst;
@@ -87,7 +99,7 @@
       } else {
         return (_lhs.getTag() == _Tag)
           ? _cmp_value(_lhs.get<_Tag>(), _rhs.get<_Tag>())
-          : _cmp_value_at<(Tag)(_Tag-1)>(_lhs, _rhs);
+          : _cmp_value_at<static_cast<Tag>(static_cast<size_t>(_Tag)-1)>(_lhs, _rhs);
       }
     }
     template <typename _Type>
@@ -130,7 +142,7 @@
       return os.str();
     }
   private:
-    Tag _tag __attribute__((aligned (1))) = booleanValue;
+    Tag _tag = booleanValue;
     union _value_t {
       _value_t() {}
       ~_value_t() {}
@@ -233,3 +245,51 @@
 }  // namespace aidl
 }  // namespace android
 }  // namespace aidl
+namespace aidl {
+namespace android {
+namespace aidl {
+namespace tests {
+[[nodiscard]] static inline std::string toString(FixedSize::FixedUnion::Tag val) {
+  switch(val) {
+  case FixedSize::FixedUnion::Tag::booleanValue:
+    return "booleanValue";
+  case FixedSize::FixedUnion::Tag::byteValue:
+    return "byteValue";
+  case FixedSize::FixedUnion::Tag::charValue:
+    return "charValue";
+  case FixedSize::FixedUnion::Tag::intValue:
+    return "intValue";
+  case FixedSize::FixedUnion::Tag::longValue:
+    return "longValue";
+  case FixedSize::FixedUnion::Tag::floatValue:
+    return "floatValue";
+  case FixedSize::FixedUnion::Tag::doubleValue:
+    return "doubleValue";
+  case FixedSize::FixedUnion::Tag::enumValue:
+    return "enumValue";
+  default:
+    return std::to_string(static_cast<int8_t>(val));
+  }
+}
+}  // namespace tests
+}  // namespace aidl
+}  // namespace android
+}  // namespace aidl
+namespace ndk {
+namespace internal {
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wc++17-extensions"
+template <>
+constexpr inline std::array<aidl::android::aidl::tests::FixedSize::FixedUnion::Tag, 8> enum_values<aidl::android::aidl::tests::FixedSize::FixedUnion::Tag> = {
+  aidl::android::aidl::tests::FixedSize::FixedUnion::Tag::booleanValue,
+  aidl::android::aidl::tests::FixedSize::FixedUnion::Tag::byteValue,
+  aidl::android::aidl::tests::FixedSize::FixedUnion::Tag::charValue,
+  aidl::android::aidl::tests::FixedSize::FixedUnion::Tag::intValue,
+  aidl::android::aidl::tests::FixedSize::FixedUnion::Tag::longValue,
+  aidl::android::aidl::tests::FixedSize::FixedUnion::Tag::floatValue,
+  aidl::android::aidl::tests::FixedSize::FixedUnion::Tag::doubleValue,
+  aidl::android::aidl::tests::FixedSize::FixedUnion::Tag::enumValue,
+};
+#pragma clang diagnostic pop
+}  // namespace internal
+}  // namespace ndk
diff --git a/tests/golden_output/aidl-test-interface-ndk-source/gen/include/aidl/android/aidl/tests/ITestService.h b/tests/golden_output/aidl-test-interface-ndk-source/gen/include/aidl/android/aidl/tests/ITestService.h
index dfa1692..2c396b8 100644
--- a/tests/golden_output/aidl-test-interface-ndk-source/gen/include/aidl/android/aidl/tests/ITestService.h
+++ b/tests/golden_output/aidl-test-interface-ndk-source/gen/include/aidl/android/aidl/tests/ITestService.h
@@ -20,6 +20,7 @@
 #include <aidl/android/aidl/tests/LongEnum.h>
 #include <aidl/android/aidl/tests/RecursiveList.h>
 #include <aidl/android/aidl/tests/StructuredParcelable.h>
+#include <aidl/android/aidl/tests/Union.h>
 #include <aidl/android/aidl/tests/extension/ExtendableParcelable.h>
 #ifdef BINDER_STABILITY_SUPPORT
 #include <android/binder_stability.h>
@@ -280,8 +281,9 @@
   static constexpr uint32_t TRANSACTION_ReverseNullableIBinderArray = FIRST_CALL_TRANSACTION + 60;
   static constexpr uint32_t TRANSACTION_GetOldNameInterface = FIRST_CALL_TRANSACTION + 61;
   static constexpr uint32_t TRANSACTION_GetNewNameInterface = FIRST_CALL_TRANSACTION + 62;
-  static constexpr uint32_t TRANSACTION_GetCppJavaTests = FIRST_CALL_TRANSACTION + 63;
-  static constexpr uint32_t TRANSACTION_getBackendType = FIRST_CALL_TRANSACTION + 64;
+  static constexpr uint32_t TRANSACTION_GetUnionTags = FIRST_CALL_TRANSACTION + 63;
+  static constexpr uint32_t TRANSACTION_GetCppJavaTests = FIRST_CALL_TRANSACTION + 64;
+  static constexpr uint32_t TRANSACTION_getBackendType = FIRST_CALL_TRANSACTION + 65;
 
   static std::shared_ptr<ITestService> fromBinder(const ::ndk::SpAIBinder& binder);
   static binder_status_t writeToParcel(AParcel* parcel, const std::shared_ptr<ITestService>& instance);
@@ -351,6 +353,7 @@
   virtual ::ndk::ScopedAStatus ReverseNullableIBinderArray(const std::optional<std::vector<::ndk::SpAIBinder>>& in_input, std::optional<std::vector<::ndk::SpAIBinder>>* out_repeated, std::optional<std::vector<::ndk::SpAIBinder>>* _aidl_return) = 0;
   virtual ::ndk::ScopedAStatus GetOldNameInterface(std::shared_ptr<::aidl::android::aidl::tests::IOldName>* _aidl_return) = 0;
   virtual ::ndk::ScopedAStatus GetNewNameInterface(std::shared_ptr<::aidl::android::aidl::tests::INewName>* _aidl_return) = 0;
+  virtual ::ndk::ScopedAStatus GetUnionTags(const std::vector<::aidl::android::aidl::tests::Union>& in_input, std::vector<::aidl::android::aidl::tests::Union::Tag>* _aidl_return) = 0;
   virtual ::ndk::ScopedAStatus GetCppJavaTests(::ndk::SpAIBinder* _aidl_return) = 0;
   virtual ::ndk::ScopedAStatus getBackendType(::aidl::android::aidl::tests::BackendType* _aidl_return) = 0;
 private:
@@ -421,6 +424,7 @@
   ::ndk::ScopedAStatus ReverseNullableIBinderArray(const std::optional<std::vector<::ndk::SpAIBinder>>& in_input, std::optional<std::vector<::ndk::SpAIBinder>>* out_repeated, std::optional<std::vector<::ndk::SpAIBinder>>* _aidl_return) override;
   ::ndk::ScopedAStatus GetOldNameInterface(std::shared_ptr<::aidl::android::aidl::tests::IOldName>* _aidl_return) override;
   ::ndk::ScopedAStatus GetNewNameInterface(std::shared_ptr<::aidl::android::aidl::tests::INewName>* _aidl_return) override;
+  ::ndk::ScopedAStatus GetUnionTags(const std::vector<::aidl::android::aidl::tests::Union>& in_input, std::vector<::aidl::android::aidl::tests::Union::Tag>* _aidl_return) override;
   ::ndk::ScopedAStatus GetCppJavaTests(::ndk::SpAIBinder* _aidl_return) override;
   ::ndk::ScopedAStatus getBackendType(::aidl::android::aidl::tests::BackendType* _aidl_return) override;
   ::ndk::SpAIBinder asBinder() override;
diff --git a/tests/golden_output/aidl-test-interface-ndk-source/gen/include/aidl/android/aidl/tests/IntEnum.h b/tests/golden_output/aidl-test-interface-ndk-source/gen/include/aidl/android/aidl/tests/IntEnum.h
index 7d29fd4..d272eb3 100644
--- a/tests/golden_output/aidl-test-interface-ndk-source/gen/include/aidl/android/aidl/tests/IntEnum.h
+++ b/tests/golden_output/aidl-test-interface-ndk-source/gen/include/aidl/android/aidl/tests/IntEnum.h
@@ -19,6 +19,7 @@
   FOO = 1000,
   BAR = 2000,
   BAZ = 2001,
+  QUX __attribute__((deprecated("do not use this"))) = 2002,
 };
 
 }  // namespace tests
@@ -29,6 +30,8 @@
 namespace android {
 namespace aidl {
 namespace tests {
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wdeprecated-declarations"
 [[nodiscard]] static inline std::string toString(IntEnum val) {
   switch(val) {
   case IntEnum::FOO:
@@ -37,10 +40,13 @@
     return "BAR";
   case IntEnum::BAZ:
     return "BAZ";
+  case IntEnum::QUX:
+    return "QUX";
   default:
     return std::to_string(static_cast<int32_t>(val));
   }
 }
+#pragma clang diagnostic pop
 }  // namespace tests
 }  // namespace aidl
 }  // namespace android
@@ -49,11 +55,13 @@
 namespace internal {
 #pragma clang diagnostic push
 #pragma clang diagnostic ignored "-Wc++17-extensions"
+#pragma clang diagnostic ignored "-Wdeprecated-declarations"
 template <>
-constexpr inline std::array<aidl::android::aidl::tests::IntEnum, 3> enum_values<aidl::android::aidl::tests::IntEnum> = {
+constexpr inline std::array<aidl::android::aidl::tests::IntEnum, 4> enum_values<aidl::android::aidl::tests::IntEnum> = {
   aidl::android::aidl::tests::IntEnum::FOO,
   aidl::android::aidl::tests::IntEnum::BAR,
   aidl::android::aidl::tests::IntEnum::BAZ,
+  aidl::android::aidl::tests::IntEnum::QUX,
 };
 #pragma clang diagnostic pop
 }  // namespace internal
diff --git a/tests/golden_output/aidl-test-interface-ndk-source/gen/include/aidl/android/aidl/tests/ListOfInterfaces.h b/tests/golden_output/aidl-test-interface-ndk-source/gen/include/aidl/android/aidl/tests/ListOfInterfaces.h
index a669820..e0dc968 100644
--- a/tests/golden_output/aidl-test-interface-ndk-source/gen/include/aidl/android/aidl/tests/ListOfInterfaces.h
+++ b/tests/golden_output/aidl-test-interface-ndk-source/gen/include/aidl/android/aidl/tests/ListOfInterfaces.h
@@ -1,5 +1,6 @@
 #pragma once
 
+#include <array>
 #include <cassert>
 #include <cstdint>
 #include <memory>
@@ -9,6 +10,7 @@
 #include <utility>
 #include <variant>
 #include <vector>
+#include <android/binder_enums.h>
 #include <android/binder_ibinder.h>
 #include <android/binder_interface_utils.h>
 #include <android/binder_parcelable_utils.h>
@@ -152,17 +154,23 @@
     typedef std::false_type fixed_size;
     static const char* descriptor;
 
-    enum Tag : int32_t {
-      iface = 0,  // android.aidl.tests.ListOfInterfaces.IEmptyInterface iface;
-      nullable_iface,  // android.aidl.tests.ListOfInterfaces.IEmptyInterface nullable_iface;
-      iface_list,  // List<android.aidl.tests.ListOfInterfaces.IEmptyInterface> iface_list;
-      nullable_iface_list,  // List<android.aidl.tests.ListOfInterfaces.IEmptyInterface> nullable_iface_list;
+    enum class Tag : int32_t {
+      iface = 0,
+      nullable_iface = 1,
+      iface_list = 2,
+      nullable_iface_list = 3,
     };
 
+    // Expose tag symbols for legacy code
+    static const inline Tag iface = Tag::iface;
+    static const inline Tag nullable_iface = Tag::nullable_iface;
+    static const inline Tag iface_list = Tag::iface_list;
+    static const inline Tag nullable_iface_list = Tag::nullable_iface_list;
+
     template<typename _Tp>
     static constexpr bool _not_self = !std::is_same_v<std::remove_cv_t<std::remove_reference_t<_Tp>>, MyUnion>;
 
-    MyUnion() : _value(std::in_place_index<iface>, std::shared_ptr<::aidl::android::aidl::tests::ListOfInterfaces::IEmptyInterface>()) { }
+    MyUnion() : _value(std::in_place_index<static_cast<size_t>(iface)>, std::shared_ptr<::aidl::android::aidl::tests::ListOfInterfaces::IEmptyInterface>()) { }
 
     template <typename _Tp, typename = std::enable_if_t<_not_self<_Tp>>>
     // NOLINTNEXTLINE(google-explicit-constructor)
@@ -175,12 +183,12 @@
 
     template <Tag _tag, typename... _Tp>
     static MyUnion make(_Tp&&... _args) {
-      return MyUnion(std::in_place_index<_tag>, std::forward<_Tp>(_args)...);
+      return MyUnion(std::in_place_index<static_cast<size_t>(_tag)>, std::forward<_Tp>(_args)...);
     }
 
     template <Tag _tag, typename _Tp, typename... _Up>
     static MyUnion make(std::initializer_list<_Tp> _il, _Up&&... _args) {
-      return MyUnion(std::in_place_index<_tag>, std::move(_il), std::forward<_Up>(_args)...);
+      return MyUnion(std::in_place_index<static_cast<size_t>(_tag)>, std::move(_il), std::forward<_Up>(_args)...);
     }
 
     Tag getTag() const {
@@ -190,18 +198,18 @@
     template <Tag _tag>
     const auto& get() const {
       if (getTag() != _tag) { __assert2(__FILE__, __LINE__, __PRETTY_FUNCTION__, "bad access: a wrong tag"); }
-      return std::get<_tag>(_value);
+      return std::get<static_cast<size_t>(_tag)>(_value);
     }
 
     template <Tag _tag>
     auto& get() {
       if (getTag() != _tag) { __assert2(__FILE__, __LINE__, __PRETTY_FUNCTION__, "bad access: a wrong tag"); }
-      return std::get<_tag>(_value);
+      return std::get<static_cast<size_t>(_tag)>(_value);
     }
 
     template <Tag _tag, typename... _Tp>
     void set(_Tp&&... _args) {
-      _value.emplace<_tag>(std::forward<_Tp>(_args)...);
+      _value.emplace<static_cast<size_t>(_tag)>(std::forward<_Tp>(_args)...);
     }
 
     binder_status_t readFromParcel(const AParcel* _parcel);
@@ -277,3 +285,39 @@
 }  // namespace aidl
 }  // namespace android
 }  // namespace aidl
+namespace aidl {
+namespace android {
+namespace aidl {
+namespace tests {
+[[nodiscard]] static inline std::string toString(ListOfInterfaces::MyUnion::Tag val) {
+  switch(val) {
+  case ListOfInterfaces::MyUnion::Tag::iface:
+    return "iface";
+  case ListOfInterfaces::MyUnion::Tag::nullable_iface:
+    return "nullable_iface";
+  case ListOfInterfaces::MyUnion::Tag::iface_list:
+    return "iface_list";
+  case ListOfInterfaces::MyUnion::Tag::nullable_iface_list:
+    return "nullable_iface_list";
+  default:
+    return std::to_string(static_cast<int32_t>(val));
+  }
+}
+}  // namespace tests
+}  // namespace aidl
+}  // namespace android
+}  // namespace aidl
+namespace ndk {
+namespace internal {
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wc++17-extensions"
+template <>
+constexpr inline std::array<aidl::android::aidl::tests::ListOfInterfaces::MyUnion::Tag, 4> enum_values<aidl::android::aidl::tests::ListOfInterfaces::MyUnion::Tag> = {
+  aidl::android::aidl::tests::ListOfInterfaces::MyUnion::Tag::iface,
+  aidl::android::aidl::tests::ListOfInterfaces::MyUnion::Tag::nullable_iface,
+  aidl::android::aidl::tests::ListOfInterfaces::MyUnion::Tag::iface_list,
+  aidl::android::aidl::tests::ListOfInterfaces::MyUnion::Tag::nullable_iface_list,
+};
+#pragma clang diagnostic pop
+}  // namespace internal
+}  // namespace ndk
diff --git a/tests/golden_output/aidl-test-interface-ndk-source/gen/include/aidl/android/aidl/tests/Union.h b/tests/golden_output/aidl-test-interface-ndk-source/gen/include/aidl/android/aidl/tests/Union.h
index 7b6c7f8..a94d03f 100644
--- a/tests/golden_output/aidl-test-interface-ndk-source/gen/include/aidl/android/aidl/tests/Union.h
+++ b/tests/golden_output/aidl-test-interface-ndk-source/gen/include/aidl/android/aidl/tests/Union.h
@@ -1,5 +1,6 @@
 #pragma once
 
+#include <array>
 #include <cassert>
 #include <cstdint>
 #include <memory>
@@ -9,6 +10,7 @@
 #include <utility>
 #include <variant>
 #include <vector>
+#include <android/binder_enums.h>
 #include <android/binder_interface_utils.h>
 #include <android/binder_parcelable_utils.h>
 #include <android/binder_to_string.h>
@@ -30,20 +32,29 @@
   typedef std::false_type fixed_size;
   static const char* descriptor;
 
-  enum Tag : int32_t {
-    ns = 0,  // int[] ns;
-    n,  // int n;
-    m,  // int m;
-    s,  // String s;
-    ibinder,  // IBinder ibinder;
-    ss,  // List<String> ss;
-    be,  // android.aidl.tests.ByteEnum be;
+  enum class Tag : int32_t {
+    ns = 0,
+    n = 1,
+    m = 2,
+    s = 3,
+    ibinder = 4,
+    ss = 5,
+    be = 6,
   };
 
+  // Expose tag symbols for legacy code
+  static const inline Tag ns = Tag::ns;
+  static const inline Tag n = Tag::n;
+  static const inline Tag m = Tag::m;
+  static const inline Tag s = Tag::s;
+  static const inline Tag ibinder = Tag::ibinder;
+  static const inline Tag ss = Tag::ss;
+  static const inline Tag be = Tag::be;
+
   template<typename _Tp>
   static constexpr bool _not_self = !std::is_same_v<std::remove_cv_t<std::remove_reference_t<_Tp>>, Union>;
 
-  Union() : _value(std::in_place_index<ns>, std::vector<int32_t>({})) { }
+  Union() : _value(std::in_place_index<static_cast<size_t>(ns)>, std::vector<int32_t>({})) { }
 
   template <typename _Tp, typename = std::enable_if_t<_not_self<_Tp>>>
   // NOLINTNEXTLINE(google-explicit-constructor)
@@ -56,12 +67,12 @@
 
   template <Tag _tag, typename... _Tp>
   static Union make(_Tp&&... _args) {
-    return Union(std::in_place_index<_tag>, std::forward<_Tp>(_args)...);
+    return Union(std::in_place_index<static_cast<size_t>(_tag)>, std::forward<_Tp>(_args)...);
   }
 
   template <Tag _tag, typename _Tp, typename... _Up>
   static Union make(std::initializer_list<_Tp> _il, _Up&&... _args) {
-    return Union(std::in_place_index<_tag>, std::move(_il), std::forward<_Up>(_args)...);
+    return Union(std::in_place_index<static_cast<size_t>(_tag)>, std::move(_il), std::forward<_Up>(_args)...);
   }
 
   Tag getTag() const {
@@ -71,18 +82,18 @@
   template <Tag _tag>
   const auto& get() const {
     if (getTag() != _tag) { __assert2(__FILE__, __LINE__, __PRETTY_FUNCTION__, "bad access: a wrong tag"); }
-    return std::get<_tag>(_value);
+    return std::get<static_cast<size_t>(_tag)>(_value);
   }
 
   template <Tag _tag>
   auto& get() {
     if (getTag() != _tag) { __assert2(__FILE__, __LINE__, __PRETTY_FUNCTION__, "bad access: a wrong tag"); }
-    return std::get<_tag>(_value);
+    return std::get<static_cast<size_t>(_tag)>(_value);
   }
 
   template <Tag _tag, typename... _Tp>
   void set(_Tp&&... _args) {
-    _value.emplace<_tag>(std::forward<_Tp>(_args)...);
+    _value.emplace<static_cast<size_t>(_tag)>(std::forward<_Tp>(_args)...);
   }
 
   binder_status_t readFromParcel(const AParcel* _parcel);
@@ -131,3 +142,48 @@
 }  // namespace aidl
 }  // namespace android
 }  // namespace aidl
+namespace aidl {
+namespace android {
+namespace aidl {
+namespace tests {
+[[nodiscard]] static inline std::string toString(Union::Tag val) {
+  switch(val) {
+  case Union::Tag::ns:
+    return "ns";
+  case Union::Tag::n:
+    return "n";
+  case Union::Tag::m:
+    return "m";
+  case Union::Tag::s:
+    return "s";
+  case Union::Tag::ibinder:
+    return "ibinder";
+  case Union::Tag::ss:
+    return "ss";
+  case Union::Tag::be:
+    return "be";
+  default:
+    return std::to_string(static_cast<int32_t>(val));
+  }
+}
+}  // namespace tests
+}  // namespace aidl
+}  // namespace android
+}  // namespace aidl
+namespace ndk {
+namespace internal {
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wc++17-extensions"
+template <>
+constexpr inline std::array<aidl::android::aidl::tests::Union::Tag, 7> enum_values<aidl::android::aidl::tests::Union::Tag> = {
+  aidl::android::aidl::tests::Union::Tag::ns,
+  aidl::android::aidl::tests::Union::Tag::n,
+  aidl::android::aidl::tests::Union::Tag::m,
+  aidl::android::aidl::tests::Union::Tag::s,
+  aidl::android::aidl::tests::Union::Tag::ibinder,
+  aidl::android::aidl::tests::Union::Tag::ss,
+  aidl::android::aidl::tests::Union::Tag::be,
+};
+#pragma clang diagnostic pop
+}  // namespace internal
+}  // namespace ndk
diff --git a/tests/golden_output/aidl-test-interface-ndk-source/gen/include/aidl/android/aidl/tests/UnionWithFd.h b/tests/golden_output/aidl-test-interface-ndk-source/gen/include/aidl/android/aidl/tests/UnionWithFd.h
index e1929bf..986b542 100644
--- a/tests/golden_output/aidl-test-interface-ndk-source/gen/include/aidl/android/aidl/tests/UnionWithFd.h
+++ b/tests/golden_output/aidl-test-interface-ndk-source/gen/include/aidl/android/aidl/tests/UnionWithFd.h
@@ -1,5 +1,6 @@
 #pragma once
 
+#include <array>
 #include <cassert>
 #include <cstdint>
 #include <memory>
@@ -9,6 +10,7 @@
 #include <utility>
 #include <variant>
 #include <vector>
+#include <android/binder_enums.h>
 #include <android/binder_interface_utils.h>
 #include <android/binder_parcelable_utils.h>
 #include <android/binder_to_string.h>
@@ -29,15 +31,19 @@
   typedef std::false_type fixed_size;
   static const char* descriptor;
 
-  enum Tag : int32_t {
-    num = 0,  // int num;
-    pfd,  // ParcelFileDescriptor pfd;
+  enum class Tag : int32_t {
+    num = 0,
+    pfd = 1,
   };
 
+  // Expose tag symbols for legacy code
+  static const inline Tag num = Tag::num;
+  static const inline Tag pfd = Tag::pfd;
+
   template<typename _Tp>
   static constexpr bool _not_self = !std::is_same_v<std::remove_cv_t<std::remove_reference_t<_Tp>>, UnionWithFd>;
 
-  UnionWithFd() : _value(std::in_place_index<num>, int32_t(0)) { }
+  UnionWithFd() : _value(std::in_place_index<static_cast<size_t>(num)>, int32_t(0)) { }
 
   template <typename _Tp, typename = std::enable_if_t<_not_self<_Tp>>>
   // NOLINTNEXTLINE(google-explicit-constructor)
@@ -50,12 +56,12 @@
 
   template <Tag _tag, typename... _Tp>
   static UnionWithFd make(_Tp&&... _args) {
-    return UnionWithFd(std::in_place_index<_tag>, std::forward<_Tp>(_args)...);
+    return UnionWithFd(std::in_place_index<static_cast<size_t>(_tag)>, std::forward<_Tp>(_args)...);
   }
 
   template <Tag _tag, typename _Tp, typename... _Up>
   static UnionWithFd make(std::initializer_list<_Tp> _il, _Up&&... _args) {
-    return UnionWithFd(std::in_place_index<_tag>, std::move(_il), std::forward<_Up>(_args)...);
+    return UnionWithFd(std::in_place_index<static_cast<size_t>(_tag)>, std::move(_il), std::forward<_Up>(_args)...);
   }
 
   Tag getTag() const {
@@ -65,18 +71,18 @@
   template <Tag _tag>
   const auto& get() const {
     if (getTag() != _tag) { __assert2(__FILE__, __LINE__, __PRETTY_FUNCTION__, "bad access: a wrong tag"); }
-    return std::get<_tag>(_value);
+    return std::get<static_cast<size_t>(_tag)>(_value);
   }
 
   template <Tag _tag>
   auto& get() {
     if (getTag() != _tag) { __assert2(__FILE__, __LINE__, __PRETTY_FUNCTION__, "bad access: a wrong tag"); }
-    return std::get<_tag>(_value);
+    return std::get<static_cast<size_t>(_tag)>(_value);
   }
 
   template <Tag _tag, typename... _Tp>
   void set(_Tp&&... _args) {
-    _value.emplace<_tag>(std::forward<_Tp>(_args)...);
+    _value.emplace<static_cast<size_t>(_tag)>(std::forward<_Tp>(_args)...);
   }
 
   binder_status_t readFromParcel(const AParcel* _parcel);
@@ -119,3 +125,33 @@
 }  // namespace aidl
 }  // namespace android
 }  // namespace aidl
+namespace aidl {
+namespace android {
+namespace aidl {
+namespace tests {
+[[nodiscard]] static inline std::string toString(UnionWithFd::Tag val) {
+  switch(val) {
+  case UnionWithFd::Tag::num:
+    return "num";
+  case UnionWithFd::Tag::pfd:
+    return "pfd";
+  default:
+    return std::to_string(static_cast<int32_t>(val));
+  }
+}
+}  // namespace tests
+}  // namespace aidl
+}  // namespace android
+}  // namespace aidl
+namespace ndk {
+namespace internal {
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wc++17-extensions"
+template <>
+constexpr inline std::array<aidl::android::aidl::tests::UnionWithFd::Tag, 2> enum_values<aidl::android::aidl::tests::UnionWithFd::Tag> = {
+  aidl::android::aidl::tests::UnionWithFd::Tag::num,
+  aidl::android::aidl::tests::UnionWithFd::Tag::pfd,
+};
+#pragma clang diagnostic pop
+}  // namespace internal
+}  // namespace ndk
diff --git a/tests/golden_output/aidl-test-interface-ndk-source/gen/include/aidl/android/aidl/tests/nested/BnNestedService.h b/tests/golden_output/aidl-test-interface-ndk-source/gen/include/aidl/android/aidl/tests/nested/BnNestedService.h
index 98ec330..4fc97ab 100644
--- a/tests/golden_output/aidl-test-interface-ndk-source/gen/include/aidl/android/aidl/tests/nested/BnNestedService.h
+++ b/tests/golden_output/aidl-test-interface-ndk-source/gen/include/aidl/android/aidl/tests/nested/BnNestedService.h
@@ -3,6 +3,13 @@
 #include "aidl/android/aidl/tests/nested/INestedService.h"
 
 #include <android/binder_ibinder.h>
+#include <cassert>
+
+#ifndef __BIONIC__
+#ifndef __assert2
+#define __assert2(a,b,c,d) ((void)0)
+#endif
+#endif
 
 namespace aidl {
 namespace android {
@@ -17,6 +24,22 @@
   ::ndk::SpAIBinder createBinder() override;
 private:
 };
+class INestedServiceDelegator : public BnNestedService {
+public:
+  explicit INestedServiceDelegator(const std::shared_ptr<INestedService> &impl) : _impl(impl) {
+  }
+
+  ::ndk::ScopedAStatus flipStatus(const ::aidl::android::aidl::tests::nested::ParcelableWithNested& in_p, ::aidl::android::aidl::tests::nested::INestedService::Result* _aidl_return) override {
+    return _impl->flipStatus(in_p, _aidl_return);
+  }
+  ::ndk::ScopedAStatus flipStatusWithCallback(::aidl::android::aidl::tests::nested::ParcelableWithNested::Status in_status, const std::shared_ptr<::aidl::android::aidl::tests::nested::INestedService::ICallback>& in_cb) override {
+    return _impl->flipStatusWithCallback(in_status, in_cb);
+  }
+protected:
+private:
+  std::shared_ptr<INestedService> _impl;
+};
+
 }  // namespace nested
 }  // namespace tests
 }  // namespace aidl
diff --git a/tests/golden_output/aidl-test-interface-ndk-source/gen/include/aidl/android/aidl/tests/permission/BnProtected.h b/tests/golden_output/aidl-test-interface-ndk-source/gen/include/aidl/android/aidl/tests/permission/BnProtected.h
deleted file mode 100644
index 150c5b8..0000000
--- a/tests/golden_output/aidl-test-interface-ndk-source/gen/include/aidl/android/aidl/tests/permission/BnProtected.h
+++ /dev/null
@@ -1,24 +0,0 @@
-#pragma once
-
-#include "aidl/android/aidl/tests/permission/IProtected.h"
-
-#include <android/binder_ibinder.h>
-
-namespace aidl {
-namespace android {
-namespace aidl {
-namespace tests {
-namespace permission {
-class BnProtected : public ::ndk::BnCInterface<IProtected> {
-public:
-  BnProtected();
-  virtual ~BnProtected();
-protected:
-  ::ndk::SpAIBinder createBinder() override;
-private:
-};
-}  // namespace permission
-}  // namespace tests
-}  // namespace aidl
-}  // namespace android
-}  // namespace aidl
diff --git a/tests/golden_output/aidl-test-interface-ndk-source/gen/include/aidl/android/aidl/tests/permission/BnProtectedInterface.h b/tests/golden_output/aidl-test-interface-ndk-source/gen/include/aidl/android/aidl/tests/permission/BnProtectedInterface.h
deleted file mode 100644
index ac2a46c..0000000
--- a/tests/golden_output/aidl-test-interface-ndk-source/gen/include/aidl/android/aidl/tests/permission/BnProtectedInterface.h
+++ /dev/null
@@ -1,24 +0,0 @@
-#pragma once
-
-#include "aidl/android/aidl/tests/permission/IProtectedInterface.h"
-
-#include <android/binder_ibinder.h>
-
-namespace aidl {
-namespace android {
-namespace aidl {
-namespace tests {
-namespace permission {
-class BnProtectedInterface : public ::ndk::BnCInterface<IProtectedInterface> {
-public:
-  BnProtectedInterface();
-  virtual ~BnProtectedInterface();
-protected:
-  ::ndk::SpAIBinder createBinder() override;
-private:
-};
-}  // namespace permission
-}  // namespace tests
-}  // namespace aidl
-}  // namespace android
-}  // namespace aidl
diff --git a/tests/golden_output/aidl-test-interface-ndk-source/gen/include/aidl/android/aidl/tests/permission/BpProtected.h b/tests/golden_output/aidl-test-interface-ndk-source/gen/include/aidl/android/aidl/tests/permission/BpProtected.h
deleted file mode 100644
index f3d8ee9..0000000
--- a/tests/golden_output/aidl-test-interface-ndk-source/gen/include/aidl/android/aidl/tests/permission/BpProtected.h
+++ /dev/null
@@ -1,25 +0,0 @@
-#pragma once
-
-#include "aidl/android/aidl/tests/permission/IProtected.h"
-
-#include <android/binder_ibinder.h>
-
-namespace aidl {
-namespace android {
-namespace aidl {
-namespace tests {
-namespace permission {
-class BpProtected : public ::ndk::BpCInterface<IProtected> {
-public:
-  explicit BpProtected(const ::ndk::SpAIBinder& binder);
-  virtual ~BpProtected();
-
-  ::ndk::ScopedAStatus PermissionProtected() override;
-  ::ndk::ScopedAStatus MultiplePermissionsAll() override;
-  ::ndk::ScopedAStatus MultiplePermissionsAny() override;
-};
-}  // namespace permission
-}  // namespace tests
-}  // namespace aidl
-}  // namespace android
-}  // namespace aidl
diff --git a/tests/golden_output/aidl-test-interface-ndk-source/gen/include/aidl/android/aidl/tests/permission/BpProtectedInterface.h b/tests/golden_output/aidl-test-interface-ndk-source/gen/include/aidl/android/aidl/tests/permission/BpProtectedInterface.h
deleted file mode 100644
index 7878862..0000000
--- a/tests/golden_output/aidl-test-interface-ndk-source/gen/include/aidl/android/aidl/tests/permission/BpProtectedInterface.h
+++ /dev/null
@@ -1,24 +0,0 @@
-#pragma once
-
-#include "aidl/android/aidl/tests/permission/IProtectedInterface.h"
-
-#include <android/binder_ibinder.h>
-
-namespace aidl {
-namespace android {
-namespace aidl {
-namespace tests {
-namespace permission {
-class BpProtectedInterface : public ::ndk::BpCInterface<IProtectedInterface> {
-public:
-  explicit BpProtectedInterface(const ::ndk::SpAIBinder& binder);
-  virtual ~BpProtectedInterface();
-
-  ::ndk::ScopedAStatus Method1() override;
-  ::ndk::ScopedAStatus Method2() override;
-};
-}  // namespace permission
-}  // namespace tests
-}  // namespace aidl
-}  // namespace android
-}  // namespace aidl
diff --git a/tests/golden_output/aidl-test-interface-ndk-source/gen/include/aidl/android/aidl/tests/permission/IProtected.h b/tests/golden_output/aidl-test-interface-ndk-source/gen/include/aidl/android/aidl/tests/permission/IProtected.h
deleted file mode 100644
index b3bc190..0000000
--- a/tests/golden_output/aidl-test-interface-ndk-source/gen/include/aidl/android/aidl/tests/permission/IProtected.h
+++ /dev/null
@@ -1,51 +0,0 @@
-#pragma once
-
-#include <cstdint>
-#include <memory>
-#include <optional>
-#include <string>
-#include <vector>
-#include <android/binder_interface_utils.h>
-#ifdef BINDER_STABILITY_SUPPORT
-#include <android/binder_stability.h>
-#endif  // BINDER_STABILITY_SUPPORT
-
-namespace aidl {
-namespace android {
-namespace aidl {
-namespace tests {
-namespace permission {
-class IProtected : public ::ndk::ICInterface {
-public:
-  static const char* descriptor;
-  IProtected();
-  virtual ~IProtected();
-
-  static constexpr uint32_t TRANSACTION_PermissionProtected = FIRST_CALL_TRANSACTION + 0;
-  static constexpr uint32_t TRANSACTION_MultiplePermissionsAll = FIRST_CALL_TRANSACTION + 1;
-  static constexpr uint32_t TRANSACTION_MultiplePermissionsAny = FIRST_CALL_TRANSACTION + 2;
-
-  static std::shared_ptr<IProtected> fromBinder(const ::ndk::SpAIBinder& binder);
-  static binder_status_t writeToParcel(AParcel* parcel, const std::shared_ptr<IProtected>& instance);
-  static binder_status_t readFromParcel(const AParcel* parcel, std::shared_ptr<IProtected>* instance);
-  static bool setDefaultImpl(const std::shared_ptr<IProtected>& impl);
-  static const std::shared_ptr<IProtected>& getDefaultImpl();
-  virtual ::ndk::ScopedAStatus PermissionProtected() = 0;
-  virtual ::ndk::ScopedAStatus MultiplePermissionsAll() = 0;
-  virtual ::ndk::ScopedAStatus MultiplePermissionsAny() = 0;
-private:
-  static std::shared_ptr<IProtected> default_impl;
-};
-class IProtectedDefault : public IProtected {
-public:
-  ::ndk::ScopedAStatus PermissionProtected() override;
-  ::ndk::ScopedAStatus MultiplePermissionsAll() override;
-  ::ndk::ScopedAStatus MultiplePermissionsAny() override;
-  ::ndk::SpAIBinder asBinder() override;
-  bool isRemote() override;
-};
-}  // namespace permission
-}  // namespace tests
-}  // namespace aidl
-}  // namespace android
-}  // namespace aidl
diff --git a/tests/golden_output/aidl-test-interface-ndk-source/gen/include/aidl/android/aidl/tests/permission/IProtectedInterface.h b/tests/golden_output/aidl-test-interface-ndk-source/gen/include/aidl/android/aidl/tests/permission/IProtectedInterface.h
deleted file mode 100644
index 2afb203..0000000
--- a/tests/golden_output/aidl-test-interface-ndk-source/gen/include/aidl/android/aidl/tests/permission/IProtectedInterface.h
+++ /dev/null
@@ -1,48 +0,0 @@
-#pragma once
-
-#include <cstdint>
-#include <memory>
-#include <optional>
-#include <string>
-#include <vector>
-#include <android/binder_interface_utils.h>
-#ifdef BINDER_STABILITY_SUPPORT
-#include <android/binder_stability.h>
-#endif  // BINDER_STABILITY_SUPPORT
-
-namespace aidl {
-namespace android {
-namespace aidl {
-namespace tests {
-namespace permission {
-class IProtectedInterface : public ::ndk::ICInterface {
-public:
-  static const char* descriptor;
-  IProtectedInterface();
-  virtual ~IProtectedInterface();
-
-  static constexpr uint32_t TRANSACTION_Method1 = FIRST_CALL_TRANSACTION + 0;
-  static constexpr uint32_t TRANSACTION_Method2 = FIRST_CALL_TRANSACTION + 1;
-
-  static std::shared_ptr<IProtectedInterface> fromBinder(const ::ndk::SpAIBinder& binder);
-  static binder_status_t writeToParcel(AParcel* parcel, const std::shared_ptr<IProtectedInterface>& instance);
-  static binder_status_t readFromParcel(const AParcel* parcel, std::shared_ptr<IProtectedInterface>* instance);
-  static bool setDefaultImpl(const std::shared_ptr<IProtectedInterface>& impl);
-  static const std::shared_ptr<IProtectedInterface>& getDefaultImpl();
-  virtual ::ndk::ScopedAStatus Method1() = 0;
-  virtual ::ndk::ScopedAStatus Method2() = 0;
-private:
-  static std::shared_ptr<IProtectedInterface> default_impl;
-};
-class IProtectedInterfaceDefault : public IProtectedInterface {
-public:
-  ::ndk::ScopedAStatus Method1() override;
-  ::ndk::ScopedAStatus Method2() override;
-  ::ndk::SpAIBinder asBinder() override;
-  bool isRemote() override;
-};
-}  // namespace permission
-}  // namespace tests
-}  // namespace aidl
-}  // namespace android
-}  // namespace aidl
diff --git a/tests/golden_output/aidl-test-interface-ndk-source/gen/include/aidl/android/aidl/tests/unions/EnumUnion.h b/tests/golden_output/aidl-test-interface-ndk-source/gen/include/aidl/android/aidl/tests/unions/EnumUnion.h
index 77c10d1..2190f50 100644
--- a/tests/golden_output/aidl-test-interface-ndk-source/gen/include/aidl/android/aidl/tests/unions/EnumUnion.h
+++ b/tests/golden_output/aidl-test-interface-ndk-source/gen/include/aidl/android/aidl/tests/unions/EnumUnion.h
@@ -1,5 +1,6 @@
 #pragma once
 
+#include <array>
 #include <cassert>
 #include <cstdint>
 #include <memory>
@@ -9,6 +10,7 @@
 #include <utility>
 #include <variant>
 #include <vector>
+#include <android/binder_enums.h>
 #include <android/binder_interface_utils.h>
 #include <android/binder_parcelable_utils.h>
 #include <android/binder_to_string.h>
@@ -32,15 +34,21 @@
   typedef std::false_type fixed_size;
   static const char* descriptor;
 
-  enum Tag : int32_t {
-    intEnum = 0,  // android.aidl.tests.IntEnum intEnum;
-    longEnum,  // android.aidl.tests.LongEnum longEnum;
+  enum class Tag : int32_t {
+    intEnum = 0,
+    longEnum = 1,
+    deprecatedField __attribute__((deprecated("do not use this"))) = 2,
   };
 
+  // Expose tag symbols for legacy code
+  static const inline Tag intEnum = Tag::intEnum;
+  static const inline Tag longEnum = Tag::longEnum;
+  static const inline Tag __attribute__((deprecated("do not use this"))) deprecatedField = Tag::deprecatedField;
+
   template<typename _Tp>
   static constexpr bool _not_self = !std::is_same_v<std::remove_cv_t<std::remove_reference_t<_Tp>>, EnumUnion>;
 
-  EnumUnion() : _value(std::in_place_index<intEnum>, ::aidl::android::aidl::tests::IntEnum(::aidl::android::aidl::tests::IntEnum::FOO)) { }
+  EnumUnion() : _value(std::in_place_index<static_cast<size_t>(intEnum)>, ::aidl::android::aidl::tests::IntEnum(::aidl::android::aidl::tests::IntEnum::FOO)) { }
 
   template <typename _Tp, typename = std::enable_if_t<_not_self<_Tp>>>
   // NOLINTNEXTLINE(google-explicit-constructor)
@@ -53,12 +61,12 @@
 
   template <Tag _tag, typename... _Tp>
   static EnumUnion make(_Tp&&... _args) {
-    return EnumUnion(std::in_place_index<_tag>, std::forward<_Tp>(_args)...);
+    return EnumUnion(std::in_place_index<static_cast<size_t>(_tag)>, std::forward<_Tp>(_args)...);
   }
 
   template <Tag _tag, typename _Tp, typename... _Up>
   static EnumUnion make(std::initializer_list<_Tp> _il, _Up&&... _args) {
-    return EnumUnion(std::in_place_index<_tag>, std::move(_il), std::forward<_Up>(_args)...);
+    return EnumUnion(std::in_place_index<static_cast<size_t>(_tag)>, std::move(_il), std::forward<_Up>(_args)...);
   }
 
   Tag getTag() const {
@@ -68,18 +76,18 @@
   template <Tag _tag>
   const auto& get() const {
     if (getTag() != _tag) { __assert2(__FILE__, __LINE__, __PRETTY_FUNCTION__, "bad access: a wrong tag"); }
-    return std::get<_tag>(_value);
+    return std::get<static_cast<size_t>(_tag)>(_value);
   }
 
   template <Tag _tag>
   auto& get() {
     if (getTag() != _tag) { __assert2(__FILE__, __LINE__, __PRETTY_FUNCTION__, "bad access: a wrong tag"); }
-    return std::get<_tag>(_value);
+    return std::get<static_cast<size_t>(_tag)>(_value);
   }
 
   template <Tag _tag, typename... _Tp>
   void set(_Tp&&... _args) {
-    _value.emplace<_tag>(std::forward<_Tp>(_args)...);
+    _value.emplace<static_cast<size_t>(_tag)>(std::forward<_Tp>(_args)...);
   }
 
   binder_status_t readFromParcel(const AParcel* _parcel);
@@ -111,15 +119,58 @@
     switch (getTag()) {
     case intEnum: os << "intEnum: " << ::android::internal::ToString(get<intEnum>()); break;
     case longEnum: os << "longEnum: " << ::android::internal::ToString(get<longEnum>()); break;
+    #pragma clang diagnostic push
+    #pragma clang diagnostic ignored "-Wdeprecated-declarations"
+    case deprecatedField: os << "deprecatedField: " << ::android::internal::ToString(get<deprecatedField>()); break;
+    #pragma clang diagnostic pop
     }
     os << "}";
     return os.str();
   }
 private:
-  std::variant<::aidl::android::aidl::tests::IntEnum, ::aidl::android::aidl::tests::LongEnum> _value;
+  std::variant<::aidl::android::aidl::tests::IntEnum, ::aidl::android::aidl::tests::LongEnum, int32_t> _value;
 };
 }  // namespace unions
 }  // namespace tests
 }  // namespace aidl
 }  // namespace android
 }  // namespace aidl
+namespace aidl {
+namespace android {
+namespace aidl {
+namespace tests {
+namespace unions {
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wdeprecated-declarations"
+[[nodiscard]] static inline std::string toString(EnumUnion::Tag val) {
+  switch(val) {
+  case EnumUnion::Tag::intEnum:
+    return "intEnum";
+  case EnumUnion::Tag::longEnum:
+    return "longEnum";
+  case EnumUnion::Tag::deprecatedField:
+    return "deprecatedField";
+  default:
+    return std::to_string(static_cast<int32_t>(val));
+  }
+}
+#pragma clang diagnostic pop
+}  // namespace unions
+}  // namespace tests
+}  // namespace aidl
+}  // namespace android
+}  // namespace aidl
+namespace ndk {
+namespace internal {
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wc++17-extensions"
+#pragma clang diagnostic ignored "-Wdeprecated-declarations"
+template <>
+constexpr inline std::array<aidl::android::aidl::tests::unions::EnumUnion::Tag, 3> enum_values<aidl::android::aidl::tests::unions::EnumUnion::Tag> = {
+  aidl::android::aidl::tests::unions::EnumUnion::Tag::intEnum,
+  aidl::android::aidl::tests::unions::EnumUnion::Tag::longEnum,
+  aidl::android::aidl::tests::unions::EnumUnion::Tag::deprecatedField,
+};
+#pragma clang diagnostic pop
+}  // namespace internal
+}  // namespace ndk
diff --git a/tests/golden_output/aidl-test-interface-ndk-source/gen/include/aidl/android/aidl/tests/unions/UnionInUnion.h b/tests/golden_output/aidl-test-interface-ndk-source/gen/include/aidl/android/aidl/tests/unions/UnionInUnion.h
index 8dc56f2..a41dfbb 100644
--- a/tests/golden_output/aidl-test-interface-ndk-source/gen/include/aidl/android/aidl/tests/unions/UnionInUnion.h
+++ b/tests/golden_output/aidl-test-interface-ndk-source/gen/include/aidl/android/aidl/tests/unions/UnionInUnion.h
@@ -1,5 +1,6 @@
 #pragma once
 
+#include <array>
 #include <cassert>
 #include <cstdint>
 #include <memory>
@@ -9,6 +10,7 @@
 #include <utility>
 #include <variant>
 #include <vector>
+#include <android/binder_enums.h>
 #include <android/binder_interface_utils.h>
 #include <android/binder_parcelable_utils.h>
 #include <android/binder_to_string.h>
@@ -31,15 +33,19 @@
   typedef std::false_type fixed_size;
   static const char* descriptor;
 
-  enum Tag : int32_t {
-    first = 0,  // android.aidl.tests.unions.EnumUnion first;
-    second,  // int second;
+  enum class Tag : int32_t {
+    first = 0,
+    second = 1,
   };
 
+  // Expose tag symbols for legacy code
+  static const inline Tag first = Tag::first;
+  static const inline Tag second = Tag::second;
+
   template<typename _Tp>
   static constexpr bool _not_self = !std::is_same_v<std::remove_cv_t<std::remove_reference_t<_Tp>>, UnionInUnion>;
 
-  UnionInUnion() : _value(std::in_place_index<first>, ::aidl::android::aidl::tests::unions::EnumUnion()) { }
+  UnionInUnion() : _value(std::in_place_index<static_cast<size_t>(first)>, ::aidl::android::aidl::tests::unions::EnumUnion()) { }
 
   template <typename _Tp, typename = std::enable_if_t<_not_self<_Tp>>>
   // NOLINTNEXTLINE(google-explicit-constructor)
@@ -52,12 +58,12 @@
 
   template <Tag _tag, typename... _Tp>
   static UnionInUnion make(_Tp&&... _args) {
-    return UnionInUnion(std::in_place_index<_tag>, std::forward<_Tp>(_args)...);
+    return UnionInUnion(std::in_place_index<static_cast<size_t>(_tag)>, std::forward<_Tp>(_args)...);
   }
 
   template <Tag _tag, typename _Tp, typename... _Up>
   static UnionInUnion make(std::initializer_list<_Tp> _il, _Up&&... _args) {
-    return UnionInUnion(std::in_place_index<_tag>, std::move(_il), std::forward<_Up>(_args)...);
+    return UnionInUnion(std::in_place_index<static_cast<size_t>(_tag)>, std::move(_il), std::forward<_Up>(_args)...);
   }
 
   Tag getTag() const {
@@ -67,18 +73,18 @@
   template <Tag _tag>
   const auto& get() const {
     if (getTag() != _tag) { __assert2(__FILE__, __LINE__, __PRETTY_FUNCTION__, "bad access: a wrong tag"); }
-    return std::get<_tag>(_value);
+    return std::get<static_cast<size_t>(_tag)>(_value);
   }
 
   template <Tag _tag>
   auto& get() {
     if (getTag() != _tag) { __assert2(__FILE__, __LINE__, __PRETTY_FUNCTION__, "bad access: a wrong tag"); }
-    return std::get<_tag>(_value);
+    return std::get<static_cast<size_t>(_tag)>(_value);
   }
 
   template <Tag _tag, typename... _Tp>
   void set(_Tp&&... _args) {
-    _value.emplace<_tag>(std::forward<_Tp>(_args)...);
+    _value.emplace<static_cast<size_t>(_tag)>(std::forward<_Tp>(_args)...);
   }
 
   binder_status_t readFromParcel(const AParcel* _parcel);
@@ -122,3 +128,35 @@
 }  // namespace aidl
 }  // namespace android
 }  // namespace aidl
+namespace aidl {
+namespace android {
+namespace aidl {
+namespace tests {
+namespace unions {
+[[nodiscard]] static inline std::string toString(UnionInUnion::Tag val) {
+  switch(val) {
+  case UnionInUnion::Tag::first:
+    return "first";
+  case UnionInUnion::Tag::second:
+    return "second";
+  default:
+    return std::to_string(static_cast<int32_t>(val));
+  }
+}
+}  // namespace unions
+}  // namespace tests
+}  // namespace aidl
+}  // namespace android
+}  // namespace aidl
+namespace ndk {
+namespace internal {
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wc++17-extensions"
+template <>
+constexpr inline std::array<aidl::android::aidl::tests::unions::UnionInUnion::Tag, 2> enum_values<aidl::android::aidl::tests::unions::UnionInUnion::Tag> = {
+  aidl::android::aidl::tests::unions::UnionInUnion::Tag::first,
+  aidl::android::aidl::tests::unions::UnionInUnion::Tag::second,
+};
+#pragma clang diagnostic pop
+}  // namespace internal
+}  // namespace ndk
diff --git a/tests/golden_output/aidl-test-interface-java-source/gen/android/aidl/tests/permission/IProtected.java b/tests/golden_output/aidl-test-interface-permission-java-source/gen/android/aidl/tests/permission/IProtected.java
similarity index 82%
rename from tests/golden_output/aidl-test-interface-java-source/gen/android/aidl/tests/permission/IProtected.java
rename to tests/golden_output/aidl-test-interface-permission-java-source/gen/android/aidl/tests/permission/IProtected.java
index 0a3ad19..c20acb4 100644
--- a/tests/golden_output/aidl-test-interface-java-source/gen/android/aidl/tests/permission/IProtected.java
+++ b/tests/golden_output/aidl-test-interface-permission-java-source/gen/android/aidl/tests/permission/IProtected.java
@@ -16,6 +16,9 @@
     @Override public void MultiplePermissionsAny() throws android.os.RemoteException
     {
     }
+    @Override public void NonManifestPermission() throws android.os.RemoteException
+    {
+    }
     @Override
     public android.os.IBinder asBinder() {
       return null;
@@ -91,6 +94,15 @@
           reply.writeNoException();
           break;
         }
+        case TRANSACTION_NonManifestPermission:
+        {
+          if ((this.permissionCheckerWrapper(android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK, this.getCallingPid(), new android.content.AttributionSource(getCallingUid(), null, null))!=true)) {
+            throw new SecurityException("Access denied, requires: android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK");
+          }
+          this.NonManifestPermission();
+          reply.writeNoException();
+          break;
+        }
         default:
         {
           return super.onTransact(code, data, reply, flags);
@@ -115,7 +127,7 @@
       }
       @Override public void PermissionProtected() throws android.os.RemoteException
       {
-        android.os.Parcel _data = android.os.Parcel.obtain(asBinder());
+        android.os.Parcel _data = android.os.Parcel.obtain();
         android.os.Parcel _reply = android.os.Parcel.obtain();
         try {
           _data.writeInterfaceToken(DESCRIPTOR);
@@ -129,7 +141,7 @@
       }
       @Override public void MultiplePermissionsAll() throws android.os.RemoteException
       {
-        android.os.Parcel _data = android.os.Parcel.obtain(asBinder());
+        android.os.Parcel _data = android.os.Parcel.obtain();
         android.os.Parcel _reply = android.os.Parcel.obtain();
         try {
           _data.writeInterfaceToken(DESCRIPTOR);
@@ -143,7 +155,7 @@
       }
       @Override public void MultiplePermissionsAny() throws android.os.RemoteException
       {
-        android.os.Parcel _data = android.os.Parcel.obtain(asBinder());
+        android.os.Parcel _data = android.os.Parcel.obtain();
         android.os.Parcel _reply = android.os.Parcel.obtain();
         try {
           _data.writeInterfaceToken(DESCRIPTOR);
@@ -155,6 +167,20 @@
           _data.recycle();
         }
       }
+      @Override public void NonManifestPermission() throws android.os.RemoteException
+      {
+        android.os.Parcel _data = android.os.Parcel.obtain();
+        android.os.Parcel _reply = android.os.Parcel.obtain();
+        try {
+          _data.writeInterfaceToken(DESCRIPTOR);
+          boolean _status = mRemote.transact(Stub.TRANSACTION_NonManifestPermission, _data, _reply, 0);
+          _reply.readException();
+        }
+        finally {
+          _reply.recycle();
+          _data.recycle();
+        }
+      }
     }
     private boolean permissionCheckerWrapper(
         String permission, int pid, android.content.AttributionSource attributionSource) {
@@ -167,6 +193,7 @@
     static final int TRANSACTION_PermissionProtected = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);
     static final int TRANSACTION_MultiplePermissionsAll = (android.os.IBinder.FIRST_CALL_TRANSACTION + 1);
     static final int TRANSACTION_MultiplePermissionsAny = (android.os.IBinder.FIRST_CALL_TRANSACTION + 2);
+    static final int TRANSACTION_NonManifestPermission = (android.os.IBinder.FIRST_CALL_TRANSACTION + 3);
   }
   public static final java.lang.String DESCRIPTOR = "android$aidl$tests$permission$IProtected".replace('$', '.');
   @android.annotation.EnforcePermission(android.Manifest.permission.READ_PHONE_STATE)
@@ -175,4 +202,6 @@
   public void MultiplePermissionsAll() throws android.os.RemoteException;
   @android.annotation.EnforcePermission(anyOf = {android.Manifest.permission.INTERNET, android.Manifest.permission.VIBRATE})
   public void MultiplePermissionsAny() throws android.os.RemoteException;
+  @android.annotation.EnforcePermission(android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK)
+  public void NonManifestPermission() throws android.os.RemoteException;
 }
diff --git a/tests/golden_output/aidl-test-interface-permission-java-source/gen/android/aidl/tests/permission/IProtected.java.d b/tests/golden_output/aidl-test-interface-permission-java-source/gen/android/aidl/tests/permission/IProtected.java.d
new file mode 100644
index 0000000..752e4bf
--- /dev/null
+++ b/tests/golden_output/aidl-test-interface-permission-java-source/gen/android/aidl/tests/permission/IProtected.java.d
@@ -0,0 +1,2 @@
+out/soong/.intermediates/system/tools/aidl/aidl-test-interface-permission-java-source/gen/android/aidl/tests/permission/IProtected.java : \
+  system/tools/aidl/tests/android/aidl/tests/permission/IProtected.aidl
diff --git a/tests/golden_output/aidl-test-interface-java-source/gen/android/aidl/tests/permission/IProtectedInterface.java b/tests/golden_output/aidl-test-interface-permission-java-source/gen/android/aidl/tests/permission/IProtectedInterface.java
similarity index 97%
rename from tests/golden_output/aidl-test-interface-java-source/gen/android/aidl/tests/permission/IProtectedInterface.java
rename to tests/golden_output/aidl-test-interface-permission-java-source/gen/android/aidl/tests/permission/IProtectedInterface.java
index 13392fd..e8d1c1b 100644
--- a/tests/golden_output/aidl-test-interface-java-source/gen/android/aidl/tests/permission/IProtectedInterface.java
+++ b/tests/golden_output/aidl-test-interface-permission-java-source/gen/android/aidl/tests/permission/IProtectedInterface.java
@@ -107,7 +107,7 @@
       }
       @Override public void Method1() throws android.os.RemoteException
       {
-        android.os.Parcel _data = android.os.Parcel.obtain(asBinder());
+        android.os.Parcel _data = android.os.Parcel.obtain();
         android.os.Parcel _reply = android.os.Parcel.obtain();
         try {
           _data.writeInterfaceToken(DESCRIPTOR);
@@ -121,7 +121,7 @@
       }
       @Override public void Method2() throws android.os.RemoteException
       {
-        android.os.Parcel _data = android.os.Parcel.obtain(asBinder());
+        android.os.Parcel _data = android.os.Parcel.obtain();
         android.os.Parcel _reply = android.os.Parcel.obtain();
         try {
           _data.writeInterfaceToken(DESCRIPTOR);
diff --git a/tests/golden_output/aidl-test-interface-permission-java-source/gen/android/aidl/tests/permission/IProtectedInterface.java.d b/tests/golden_output/aidl-test-interface-permission-java-source/gen/android/aidl/tests/permission/IProtectedInterface.java.d
new file mode 100644
index 0000000..1a36e38
--- /dev/null
+++ b/tests/golden_output/aidl-test-interface-permission-java-source/gen/android/aidl/tests/permission/IProtectedInterface.java.d
@@ -0,0 +1,2 @@
+out/soong/.intermediates/system/tools/aidl/aidl-test-interface-permission-java-source/gen/android/aidl/tests/permission/IProtectedInterface.java : \
+  system/tools/aidl/tests/android/aidl/tests/permission/IProtectedInterface.aidl
diff --git a/tests/golden_output/aidl-test-interface-permission-java-source/gen/android/aidl/tests/permission/platform/IProtected.java b/tests/golden_output/aidl-test-interface-permission-java-source/gen/android/aidl/tests/permission/platform/IProtected.java
new file mode 100644
index 0000000..7b6d96f
--- /dev/null
+++ b/tests/golden_output/aidl-test-interface-permission-java-source/gen/android/aidl/tests/permission/platform/IProtected.java
@@ -0,0 +1,124 @@
+/*
+ * This file is auto-generated.  DO NOT MODIFY.
+ */
+package android.aidl.tests.permission.platform;
+public interface IProtected extends android.os.IInterface
+{
+  /** Default implementation for IProtected. */
+  public static class Default implements android.aidl.tests.permission.platform.IProtected
+  {
+    @Override public void ProtectedWithSourceAttribution(android.content.AttributionSource source) throws android.os.RemoteException
+    {
+    }
+    @Override
+    public android.os.IBinder asBinder() {
+      return null;
+    }
+  }
+  /** Local-side IPC implementation stub class. */
+  public static abstract class Stub extends android.os.Binder implements android.aidl.tests.permission.platform.IProtected
+  {
+    /** Construct the stub at attach it to the interface. */
+    public Stub()
+    {
+      this.attachInterface(this, DESCRIPTOR);
+    }
+    /**
+     * Cast an IBinder object into an android.aidl.tests.permission.platform.IProtected interface,
+     * generating a proxy if needed.
+     */
+    public static android.aidl.tests.permission.platform.IProtected asInterface(android.os.IBinder obj)
+    {
+      if ((obj==null)) {
+        return null;
+      }
+      android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
+      if (((iin!=null)&&(iin instanceof android.aidl.tests.permission.platform.IProtected))) {
+        return ((android.aidl.tests.permission.platform.IProtected)iin);
+      }
+      return new android.aidl.tests.permission.platform.IProtected.Stub.Proxy(obj);
+    }
+    @Override public android.os.IBinder asBinder()
+    {
+      return this;
+    }
+    @Override public boolean onTransact(int code, android.os.Parcel data, android.os.Parcel reply, int flags) throws android.os.RemoteException
+    {
+      java.lang.String descriptor = DESCRIPTOR;
+      if (code >= android.os.IBinder.FIRST_CALL_TRANSACTION && code <= android.os.IBinder.LAST_CALL_TRANSACTION) {
+        data.enforceInterface(descriptor);
+      }
+      switch (code)
+      {
+        case INTERFACE_TRANSACTION:
+        {
+          reply.writeString(descriptor);
+          return true;
+        }
+      }
+      switch (code)
+      {
+        case TRANSACTION_ProtectedWithSourceAttribution:
+        {
+          android.content.AttributionSource _arg0;
+          _arg0 = data.readTypedObject(android.content.AttributionSource.CREATOR);
+          data.enforceNoDataAvail();
+          if (((this.permissionCheckerWrapper(android.Manifest.permission.INTERNET, this.getCallingPid(), _arg0)&&this.permissionCheckerWrapper(android.Manifest.permission.VIBRATE, this.getCallingPid(), _arg0))!=true)) {
+            throw new SecurityException("Access denied, requires: allOf = {android.Manifest.permission.INTERNET, android.Manifest.permission.VIBRATE}");
+          }
+          this.ProtectedWithSourceAttribution(_arg0);
+          reply.writeNoException();
+          break;
+        }
+        default:
+        {
+          return super.onTransact(code, data, reply, flags);
+        }
+      }
+      return true;
+    }
+    private static class Proxy implements android.aidl.tests.permission.platform.IProtected
+    {
+      private android.os.IBinder mRemote;
+      Proxy(android.os.IBinder remote)
+      {
+        mRemote = remote;
+      }
+      @Override public android.os.IBinder asBinder()
+      {
+        return mRemote;
+      }
+      public java.lang.String getInterfaceDescriptor()
+      {
+        return DESCRIPTOR;
+      }
+      @Override public void ProtectedWithSourceAttribution(android.content.AttributionSource source) throws android.os.RemoteException
+      {
+        android.os.Parcel _data = android.os.Parcel.obtain();
+        android.os.Parcel _reply = android.os.Parcel.obtain();
+        try {
+          _data.writeInterfaceToken(DESCRIPTOR);
+          _data.writeTypedObject(source, 0);
+          boolean _status = mRemote.transact(Stub.TRANSACTION_ProtectedWithSourceAttribution, _data, _reply, 0);
+          _reply.readException();
+        }
+        finally {
+          _reply.recycle();
+          _data.recycle();
+        }
+      }
+    }
+    private boolean permissionCheckerWrapper(
+        String permission, int pid, android.content.AttributionSource attributionSource) {
+      android.content.Context ctx =
+          android.app.ActivityThread.currentActivityThread().getSystemContext();
+      return (android.content.PermissionChecker.checkPermissionForDataDelivery(
+              ctx, permission, pid, attributionSource, "" /*message*/) ==
+          android.content.PermissionChecker.PERMISSION_GRANTED);
+    }
+    static final int TRANSACTION_ProtectedWithSourceAttribution = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);
+  }
+  public static final java.lang.String DESCRIPTOR = "android$aidl$tests$permission$platform$IProtected".replace('$', '.');
+  @android.annotation.EnforcePermission(allOf = {android.Manifest.permission.INTERNET, android.Manifest.permission.VIBRATE})
+  public void ProtectedWithSourceAttribution(android.content.AttributionSource source) throws android.os.RemoteException;
+}
diff --git a/tests/golden_output/aidl-test-interface-permission-java-source/gen/android/aidl/tests/permission/platform/IProtected.java.d b/tests/golden_output/aidl-test-interface-permission-java-source/gen/android/aidl/tests/permission/platform/IProtected.java.d
new file mode 100644
index 0000000..86866a2
--- /dev/null
+++ b/tests/golden_output/aidl-test-interface-permission-java-source/gen/android/aidl/tests/permission/platform/IProtected.java.d
@@ -0,0 +1,3 @@
+out/soong/.intermediates/system/tools/aidl/aidl-test-interface-permission-java-source/gen/android/aidl/tests/permission/platform/IProtected.java : \
+  system/tools/aidl/tests/android/aidl/tests/permission/platform/IProtected.aidl \
+  frameworks/base/core/java/android/content/AttributionSource.aidl
diff --git a/tests/golden_output/aidl-test-interface-permission-java-source/gen/timestamp b/tests/golden_output/aidl-test-interface-permission-java-source/gen/timestamp
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/golden_output/aidl-test-interface-permission-java-source/gen/timestamp
diff --git a/tests/golden_output/aidl-test-interface-rust-source/gen/android/aidl/tests/ArrayOfInterfaces.rs b/tests/golden_output/aidl-test-interface-rust-source/gen/android/aidl/tests/ArrayOfInterfaces.rs
index ee4b1f1..42b05dc 100644
--- a/tests/golden_output/aidl-test-interface-rust-source/gen/android/aidl/tests/ArrayOfInterfaces.rs
+++ b/tests/golden_output/aidl-test-interface-rust-source/gen/android/aidl/tests/ArrayOfInterfaces.rs
@@ -384,6 +384,18 @@
   impl binder::binder_impl::ParcelableMetadata for MyUnion {
     fn get_descriptor() -> &'static str { "android.aidl.tests.ArrayOfInterfaces.MyUnion" }
   }
+  pub mod Tag {
+    #![allow(non_upper_case_globals)]
+    use binder::declare_binder_enum;
+    declare_binder_enum! {
+      Tag : [i32; 4] {
+        iface = 0,
+        nullable_iface = 1,
+        iface_array = 2,
+        nullable_iface_array = 3,
+      }
+    }
+  }
 }
 pub(crate) mod mangled {
  pub use super::ArrayOfInterfaces as _7_android_4_aidl_5_tests_17_ArrayOfInterfaces;
@@ -391,4 +403,5 @@
  pub use super::IMyInterface::IMyInterface as _7_android_4_aidl_5_tests_17_ArrayOfInterfaces_12_IMyInterface;
  pub use super::MyParcelable::MyParcelable as _7_android_4_aidl_5_tests_17_ArrayOfInterfaces_12_MyParcelable;
  pub use super::MyUnion::MyUnion as _7_android_4_aidl_5_tests_17_ArrayOfInterfaces_7_MyUnion;
+ pub use super::MyUnion::Tag::Tag as _7_android_4_aidl_5_tests_17_ArrayOfInterfaces_7_MyUnion_3_Tag;
 }
diff --git a/tests/golden_output/aidl-test-interface-rust-source/gen/android/aidl/tests/FixedSize.rs b/tests/golden_output/aidl-test-interface-rust-source/gen/android/aidl/tests/FixedSize.rs
index ef73f72..2ea4430 100644
--- a/tests/golden_output/aidl-test-interface-rust-source/gen/android/aidl/tests/FixedSize.rs
+++ b/tests/golden_output/aidl-test-interface-rust-source/gen/android/aidl/tests/FixedSize.rs
@@ -216,9 +216,26 @@
   impl binder::binder_impl::ParcelableMetadata for FixedUnion {
     fn get_descriptor() -> &'static str { "android.aidl.tests.FixedSize.FixedUnion" }
   }
+  pub mod Tag {
+    #![allow(non_upper_case_globals)]
+    use binder::declare_binder_enum;
+    declare_binder_enum! {
+      Tag : [i8; 8] {
+        booleanValue = 0,
+        byteValue = 1,
+        charValue = 2,
+        intValue = 3,
+        longValue = 4,
+        floatValue = 5,
+        doubleValue = 6,
+        enumValue = 7,
+      }
+    }
+  }
 }
 pub(crate) mod mangled {
  pub use super::FixedSize as _7_android_4_aidl_5_tests_9_FixedSize;
  pub use super::FixedParcelable::FixedParcelable as _7_android_4_aidl_5_tests_9_FixedSize_15_FixedParcelable;
  pub use super::FixedUnion::FixedUnion as _7_android_4_aidl_5_tests_9_FixedSize_10_FixedUnion;
+ pub use super::FixedUnion::Tag::Tag as _7_android_4_aidl_5_tests_9_FixedSize_10_FixedUnion_3_Tag;
 }
diff --git a/tests/golden_output/aidl-test-interface-rust-source/gen/android/aidl/tests/ITestService.rs b/tests/golden_output/aidl-test-interface-rust-source/gen/android/aidl/tests/ITestService.rs
index 9e8fd92..36c03d4 100644
--- a/tests/golden_output/aidl-test-interface-rust-source/gen/android/aidl/tests/ITestService.rs
+++ b/tests/golden_output/aidl-test-interface-rust-source/gen/android/aidl/tests/ITestService.rs
@@ -78,6 +78,7 @@
   fn ReverseNullableIBinderArray(&self, _arg_input: Option<&[Option<binder::SpIBinder>]>, _arg_repeated: &mut Option<Vec<Option<binder::SpIBinder>>>) -> binder::Result<Option<Vec<Option<binder::SpIBinder>>>>;
   fn GetOldNameInterface(&self) -> binder::Result<binder::Strong<dyn crate::mangled::_7_android_4_aidl_5_tests_8_IOldName>>;
   fn GetNewNameInterface(&self) -> binder::Result<binder::Strong<dyn crate::mangled::_7_android_4_aidl_5_tests_8_INewName>>;
+  fn GetUnionTags(&self, _arg_input: &[crate::mangled::_7_android_4_aidl_5_tests_5_Union]) -> binder::Result<Vec<crate::mangled::_7_android_4_aidl_5_tests_5_Union_3_Tag>>;
   fn GetCppJavaTests(&self) -> binder::Result<Option<binder::SpIBinder>>;
   fn getBackendType(&self) -> binder::Result<crate::mangled::_7_android_4_aidl_5_tests_11_BackendType>;
   fn getDefaultImpl() -> ITestServiceDefaultRef where Self: Sized {
@@ -153,6 +154,7 @@
   fn ReverseNullableIBinderArray<'a>(&'a self, _arg_input: Option<&'a [Option<binder::SpIBinder>]>, _arg_repeated: &'a mut Option<Vec<Option<binder::SpIBinder>>>) -> binder::BoxFuture<'a, binder::Result<Option<Vec<Option<binder::SpIBinder>>>>>;
   fn GetOldNameInterface<'a>(&'a self) -> binder::BoxFuture<'a, binder::Result<binder::Strong<dyn crate::mangled::_7_android_4_aidl_5_tests_8_IOldName>>>;
   fn GetNewNameInterface<'a>(&'a self) -> binder::BoxFuture<'a, binder::Result<binder::Strong<dyn crate::mangled::_7_android_4_aidl_5_tests_8_INewName>>>;
+  fn GetUnionTags<'a>(&'a self, _arg_input: &'a [crate::mangled::_7_android_4_aidl_5_tests_5_Union]) -> binder::BoxFuture<'a, binder::Result<Vec<crate::mangled::_7_android_4_aidl_5_tests_5_Union_3_Tag>>>;
   fn GetCppJavaTests<'a>(&'a self) -> binder::BoxFuture<'a, binder::Result<Option<binder::SpIBinder>>>;
   fn getBackendType<'a>(&'a self) -> binder::BoxFuture<'a, binder::Result<crate::mangled::_7_android_4_aidl_5_tests_11_BackendType>>;
 }
@@ -223,6 +225,7 @@
   async fn ReverseNullableIBinderArray(&self, _arg_input: Option<&[Option<binder::SpIBinder>]>, _arg_repeated: &mut Option<Vec<Option<binder::SpIBinder>>>) -> binder::Result<Option<Vec<Option<binder::SpIBinder>>>>;
   async fn GetOldNameInterface(&self) -> binder::Result<binder::Strong<dyn crate::mangled::_7_android_4_aidl_5_tests_8_IOldName>>;
   async fn GetNewNameInterface(&self) -> binder::Result<binder::Strong<dyn crate::mangled::_7_android_4_aidl_5_tests_8_INewName>>;
+  async fn GetUnionTags(&self, _arg_input: &[crate::mangled::_7_android_4_aidl_5_tests_5_Union]) -> binder::Result<Vec<crate::mangled::_7_android_4_aidl_5_tests_5_Union_3_Tag>>;
   async fn GetCppJavaTests(&self) -> binder::Result<Option<binder::SpIBinder>>;
   async fn getBackendType(&self) -> binder::Result<crate::mangled::_7_android_4_aidl_5_tests_11_BackendType>;
 }
@@ -435,6 +438,9 @@
       fn GetNewNameInterface(&self) -> binder::Result<binder::Strong<dyn crate::mangled::_7_android_4_aidl_5_tests_8_INewName>> {
         self._rt.block_on(self._inner.GetNewNameInterface())
       }
+      fn GetUnionTags(&self, _arg_input: &[crate::mangled::_7_android_4_aidl_5_tests_5_Union]) -> binder::Result<Vec<crate::mangled::_7_android_4_aidl_5_tests_5_Union_3_Tag>> {
+        self._rt.block_on(self._inner.GetUnionTags(_arg_input))
+      }
       fn GetCppJavaTests(&self) -> binder::Result<Option<binder::SpIBinder>> {
         self._rt.block_on(self._inner.GetCppJavaTests())
       }
@@ -636,6 +642,9 @@
   fn GetNewNameInterface(&self) -> binder::Result<binder::Strong<dyn crate::mangled::_7_android_4_aidl_5_tests_8_INewName>> {
     Err(binder::StatusCode::UNKNOWN_TRANSACTION.into())
   }
+  fn GetUnionTags(&self, _arg_input: &[crate::mangled::_7_android_4_aidl_5_tests_5_Union]) -> binder::Result<Vec<crate::mangled::_7_android_4_aidl_5_tests_5_Union_3_Tag>> {
+    Err(binder::StatusCode::UNKNOWN_TRANSACTION.into())
+  }
   fn GetCppJavaTests(&self) -> binder::Result<Option<binder::SpIBinder>> {
     Err(binder::StatusCode::UNKNOWN_TRANSACTION.into())
   }
@@ -707,8 +716,9 @@
   pub const ReverseNullableIBinderArray: binder::binder_impl::TransactionCode = binder::binder_impl::FIRST_CALL_TRANSACTION + 60;
   pub const GetOldNameInterface: binder::binder_impl::TransactionCode = binder::binder_impl::FIRST_CALL_TRANSACTION + 61;
   pub const GetNewNameInterface: binder::binder_impl::TransactionCode = binder::binder_impl::FIRST_CALL_TRANSACTION + 62;
-  pub const GetCppJavaTests: binder::binder_impl::TransactionCode = binder::binder_impl::FIRST_CALL_TRANSACTION + 63;
-  pub const getBackendType: binder::binder_impl::TransactionCode = binder::binder_impl::FIRST_CALL_TRANSACTION + 64;
+  pub const GetUnionTags: binder::binder_impl::TransactionCode = binder::binder_impl::FIRST_CALL_TRANSACTION + 63;
+  pub const GetCppJavaTests: binder::binder_impl::TransactionCode = binder::binder_impl::FIRST_CALL_TRANSACTION + 64;
+  pub const getBackendType: binder::binder_impl::TransactionCode = binder::binder_impl::FIRST_CALL_TRANSACTION + 65;
 }
 pub type ITestServiceDefaultRef = Option<std::sync::Arc<dyn ITestServiceDefault>>;
 use lazy_static::lazy_static;
@@ -1949,6 +1959,24 @@
     let _aidl_return: binder::Strong<dyn crate::mangled::_7_android_4_aidl_5_tests_8_INewName> = _aidl_reply.read()?;
     Ok(_aidl_return)
   }
+  fn build_parcel_GetUnionTags(&self, _arg_input: &[crate::mangled::_7_android_4_aidl_5_tests_5_Union]) -> binder::Result<binder::binder_impl::Parcel> {
+    let mut aidl_data = self.binder.prepare_transact()?;
+    aidl_data.mark_sensitive();
+    aidl_data.write(_arg_input)?;
+    Ok(aidl_data)
+  }
+  fn read_response_GetUnionTags(&self, _arg_input: &[crate::mangled::_7_android_4_aidl_5_tests_5_Union], _aidl_reply: std::result::Result<binder::binder_impl::Parcel, binder::StatusCode>) -> binder::Result<Vec<crate::mangled::_7_android_4_aidl_5_tests_5_Union_3_Tag>> {
+    if let Err(binder::StatusCode::UNKNOWN_TRANSACTION) = _aidl_reply {
+      if let Some(_aidl_default_impl) = <Self as ITestService>::getDefaultImpl() {
+        return _aidl_default_impl.GetUnionTags(_arg_input);
+      }
+    }
+    let _aidl_reply = _aidl_reply?;
+    let _aidl_status: binder::Status = _aidl_reply.read()?;
+    if !_aidl_status.is_ok() { return Err(_aidl_status); }
+    let _aidl_return: Vec<crate::mangled::_7_android_4_aidl_5_tests_5_Union_3_Tag> = _aidl_reply.read()?;
+    Ok(_aidl_return)
+  }
   fn build_parcel_GetCppJavaTests(&self) -> binder::Result<binder::binder_impl::Parcel> {
     let mut aidl_data = self.binder.prepare_transact()?;
     aidl_data.mark_sensitive();
@@ -2300,6 +2328,11 @@
     let _aidl_reply = self.binder.submit_transact(transactions::GetNewNameInterface, _aidl_data, binder::binder_impl::FLAG_CLEAR_BUF | binder::binder_impl::FLAG_PRIVATE_LOCAL);
     self.read_response_GetNewNameInterface(_aidl_reply)
   }
+  fn GetUnionTags(&self, _arg_input: &[crate::mangled::_7_android_4_aidl_5_tests_5_Union]) -> binder::Result<Vec<crate::mangled::_7_android_4_aidl_5_tests_5_Union_3_Tag>> {
+    let _aidl_data = self.build_parcel_GetUnionTags(_arg_input)?;
+    let _aidl_reply = self.binder.submit_transact(transactions::GetUnionTags, _aidl_data, binder::binder_impl::FLAG_CLEAR_BUF | binder::binder_impl::FLAG_PRIVATE_LOCAL);
+    self.read_response_GetUnionTags(_arg_input, _aidl_reply)
+  }
   fn GetCppJavaTests(&self) -> binder::Result<Option<binder::SpIBinder>> {
     let _aidl_data = self.build_parcel_GetCppJavaTests()?;
     let _aidl_reply = self.binder.submit_transact(transactions::GetCppJavaTests, _aidl_data, binder::binder_impl::FLAG_CLEAR_BUF | binder::binder_impl::FLAG_PRIVATE_LOCAL);
@@ -3126,6 +3159,19 @@
       }
     )
   }
+  fn GetUnionTags<'a>(&'a self, _arg_input: &'a [crate::mangled::_7_android_4_aidl_5_tests_5_Union]) -> binder::BoxFuture<'a, binder::Result<Vec<crate::mangled::_7_android_4_aidl_5_tests_5_Union_3_Tag>>> {
+    let _aidl_data = match self.build_parcel_GetUnionTags(_arg_input) {
+      Ok(_aidl_data) => _aidl_data,
+      Err(err) => return Box::pin(std::future::ready(Err(err))),
+    };
+    let binder = self.binder.clone();
+    P::spawn(
+      move || binder.submit_transact(transactions::GetUnionTags, _aidl_data, binder::binder_impl::FLAG_CLEAR_BUF | binder::binder_impl::FLAG_PRIVATE_LOCAL),
+      move |_aidl_reply| async move {
+        self.read_response_GetUnionTags(_arg_input, _aidl_reply)
+      }
+    )
+  }
   fn GetCppJavaTests<'a>(&'a self) -> binder::BoxFuture<'a, binder::Result<Option<binder::SpIBinder>>> {
     let _aidl_data = match self.build_parcel_GetCppJavaTests() {
       Ok(_aidl_data) => _aidl_data,
@@ -3217,6 +3263,7 @@
   fn ReverseNullableIBinderArray(&self, _arg_input: Option<&[Option<binder::SpIBinder>]>, _arg_repeated: &mut Option<Vec<Option<binder::SpIBinder>>>) -> binder::Result<Option<Vec<Option<binder::SpIBinder>>>> { self.0.ReverseNullableIBinderArray(_arg_input, _arg_repeated) }
   fn GetOldNameInterface(&self) -> binder::Result<binder::Strong<dyn crate::mangled::_7_android_4_aidl_5_tests_8_IOldName>> { self.0.GetOldNameInterface() }
   fn GetNewNameInterface(&self) -> binder::Result<binder::Strong<dyn crate::mangled::_7_android_4_aidl_5_tests_8_INewName>> { self.0.GetNewNameInterface() }
+  fn GetUnionTags(&self, _arg_input: &[crate::mangled::_7_android_4_aidl_5_tests_5_Union]) -> binder::Result<Vec<crate::mangled::_7_android_4_aidl_5_tests_5_Union_3_Tag>> { self.0.GetUnionTags(_arg_input) }
   fn GetCppJavaTests(&self) -> binder::Result<Option<binder::SpIBinder>> { self.0.GetCppJavaTests() }
   fn getBackendType(&self) -> binder::Result<crate::mangled::_7_android_4_aidl_5_tests_11_BackendType> { self.0.getBackendType() }
 }
@@ -4019,6 +4066,18 @@
       }
       Ok(())
     }
+    transactions::GetUnionTags => {
+      let _arg_input: Vec<crate::mangled::_7_android_4_aidl_5_tests_5_Union> = _aidl_data.read()?;
+      let _aidl_return = _aidl_service.GetUnionTags(&_arg_input);
+      match &_aidl_return {
+        Ok(_aidl_return) => {
+          _aidl_reply.write(&binder::Status::from(binder::StatusCode::OK))?;
+          _aidl_reply.write(_aidl_return)?;
+        }
+        Err(_aidl_status) => _aidl_reply.write(_aidl_status)?
+      }
+      Ok(())
+    }
     transactions::GetCppJavaTests => {
       let _aidl_return = _aidl_service.GetCppJavaTests();
       match &_aidl_return {
diff --git a/tests/golden_output/aidl-test-interface-rust-source/gen/android/aidl/tests/ITestService.rs.d b/tests/golden_output/aidl-test-interface-rust-source/gen/android/aidl/tests/ITestService.rs.d
index 3f2111f..9786dc1 100644
--- a/tests/golden_output/aidl-test-interface-rust-source/gen/android/aidl/tests/ITestService.rs.d
+++ b/tests/golden_output/aidl-test-interface-rust-source/gen/android/aidl/tests/ITestService.rs.d
@@ -9,6 +9,6 @@
   system/tools/aidl/tests/android/aidl/tests/LongEnum.aidl \
   system/tools/aidl/tests/android/aidl/tests/RecursiveList.aidl \
   system/tools/aidl/tests/android/aidl/tests/StructuredParcelable.aidl \
+  system/tools/aidl/tests/android/aidl/tests/Union.aidl \
   system/tools/aidl/tests/android/aidl/tests/extension/ExtendableParcelable.aidl \
-  system/tools/aidl/tests/android/aidl/tests/ConstantExpressionEnum.aidl \
-  system/tools/aidl/tests/android/aidl/tests/Union.aidl
+  system/tools/aidl/tests/android/aidl/tests/ConstantExpressionEnum.aidl
diff --git a/tests/golden_output/aidl-test-interface-rust-source/gen/android/aidl/tests/IntEnum.rs b/tests/golden_output/aidl-test-interface-rust-source/gen/android/aidl/tests/IntEnum.rs
index 5ff4d95..70e71dd 100644
--- a/tests/golden_output/aidl-test-interface-rust-source/gen/android/aidl/tests/IntEnum.rs
+++ b/tests/golden_output/aidl-test-interface-rust-source/gen/android/aidl/tests/IntEnum.rs
@@ -3,10 +3,12 @@
 #![allow(non_upper_case_globals)]
 use binder::declare_binder_enum;
 declare_binder_enum! {
-  IntEnum : [i32; 3] {
+  IntEnum : [i32; 4] {
     FOO = 1000,
     BAR = 2000,
     BAZ = 2001,
+    #[deprecated = "do not use this"]
+    QUX = 2002,
   }
 }
 pub(crate) mod mangled {
diff --git a/tests/golden_output/aidl-test-interface-rust-source/gen/android/aidl/tests/ListOfInterfaces.rs b/tests/golden_output/aidl-test-interface-rust-source/gen/android/aidl/tests/ListOfInterfaces.rs
index 274006e..4f5d5c6 100644
--- a/tests/golden_output/aidl-test-interface-rust-source/gen/android/aidl/tests/ListOfInterfaces.rs
+++ b/tests/golden_output/aidl-test-interface-rust-source/gen/android/aidl/tests/ListOfInterfaces.rs
@@ -380,6 +380,18 @@
   impl binder::binder_impl::ParcelableMetadata for MyUnion {
     fn get_descriptor() -> &'static str { "android.aidl.tests.ListOfInterfaces.MyUnion" }
   }
+  pub mod Tag {
+    #![allow(non_upper_case_globals)]
+    use binder::declare_binder_enum;
+    declare_binder_enum! {
+      Tag : [i32; 4] {
+        iface = 0,
+        nullable_iface = 1,
+        iface_list = 2,
+        nullable_iface_list = 3,
+      }
+    }
+  }
 }
 pub(crate) mod mangled {
  pub use super::ListOfInterfaces as _7_android_4_aidl_5_tests_16_ListOfInterfaces;
@@ -387,4 +399,5 @@
  pub use super::IMyInterface::IMyInterface as _7_android_4_aidl_5_tests_16_ListOfInterfaces_12_IMyInterface;
  pub use super::MyParcelable::MyParcelable as _7_android_4_aidl_5_tests_16_ListOfInterfaces_12_MyParcelable;
  pub use super::MyUnion::MyUnion as _7_android_4_aidl_5_tests_16_ListOfInterfaces_7_MyUnion;
+ pub use super::MyUnion::Tag::Tag as _7_android_4_aidl_5_tests_16_ListOfInterfaces_7_MyUnion_3_Tag;
 }
diff --git a/tests/golden_output/aidl-test-interface-rust-source/gen/android/aidl/tests/Union.rs b/tests/golden_output/aidl-test-interface-rust-source/gen/android/aidl/tests/Union.rs
index 44f1f78..a2f9295 100644
--- a/tests/golden_output/aidl-test-interface-rust-source/gen/android/aidl/tests/Union.rs
+++ b/tests/golden_output/aidl-test-interface-rust-source/gen/android/aidl/tests/Union.rs
@@ -98,6 +98,22 @@
 impl binder::binder_impl::ParcelableMetadata for Union {
   fn get_descriptor() -> &'static str { "android.aidl.tests.Union" }
 }
+pub mod Tag {
+  #![allow(non_upper_case_globals)]
+  use binder::declare_binder_enum;
+  declare_binder_enum! {
+    Tag : [i32; 7] {
+      ns = 0,
+      n = 1,
+      m = 2,
+      s = 3,
+      ibinder = 4,
+      ss = 5,
+      be = 6,
+    }
+  }
+}
 pub(crate) mod mangled {
  pub use super::Union as _7_android_4_aidl_5_tests_5_Union;
+ pub use super::Tag::Tag as _7_android_4_aidl_5_tests_5_Union_3_Tag;
 }
diff --git a/tests/golden_output/aidl-test-interface-rust-source/gen/android/aidl/tests/UnionWithFd.rs b/tests/golden_output/aidl-test-interface-rust-source/gen/android/aidl/tests/UnionWithFd.rs
index 29af5a3..49f9f16 100644
--- a/tests/golden_output/aidl-test-interface-rust-source/gen/android/aidl/tests/UnionWithFd.rs
+++ b/tests/golden_output/aidl-test-interface-rust-source/gen/android/aidl/tests/UnionWithFd.rs
@@ -48,6 +48,17 @@
 impl binder::binder_impl::ParcelableMetadata for UnionWithFd {
   fn get_descriptor() -> &'static str { "android.aidl.tests.UnionWithFd" }
 }
+pub mod Tag {
+  #![allow(non_upper_case_globals)]
+  use binder::declare_binder_enum;
+  declare_binder_enum! {
+    Tag : [i32; 2] {
+      num = 0,
+      pfd = 1,
+    }
+  }
+}
 pub(crate) mod mangled {
  pub use super::UnionWithFd as _7_android_4_aidl_5_tests_11_UnionWithFd;
+ pub use super::Tag::Tag as _7_android_4_aidl_5_tests_11_UnionWithFd_3_Tag;
 }
diff --git a/tests/golden_output/aidl-test-interface-rust-source/gen/android/aidl/tests/permission/IProtected.rs b/tests/golden_output/aidl-test-interface-rust-source/gen/android/aidl/tests/permission/IProtected.rs
deleted file mode 100644
index 0abf8c1..0000000
--- a/tests/golden_output/aidl-test-interface-rust-source/gen/android/aidl/tests/permission/IProtected.rs
+++ /dev/null
@@ -1,242 +0,0 @@
-#![forbid(unsafe_code)]
-#![rustfmt::skip]
-#![allow(non_upper_case_globals)]
-#![allow(non_snake_case)]
-#[allow(unused_imports)] use binder::binder_impl::IBinderInternal;
-use binder::declare_binder_interface;
-declare_binder_interface! {
-  IProtected["android.aidl.tests.permission.IProtected"] {
-    native: BnProtected(on_transact),
-    proxy: BpProtected {
-    },
-    async: IProtectedAsync,
-  }
-}
-pub trait IProtected: binder::Interface + Send {
-  fn get_descriptor() -> &'static str where Self: Sized { "android.aidl.tests.permission.IProtected" }
-  fn PermissionProtected(&self) -> binder::Result<()>;
-  fn MultiplePermissionsAll(&self) -> binder::Result<()>;
-  fn MultiplePermissionsAny(&self) -> binder::Result<()>;
-  fn getDefaultImpl() -> IProtectedDefaultRef where Self: Sized {
-    DEFAULT_IMPL.lock().unwrap().clone()
-  }
-  fn setDefaultImpl(d: IProtectedDefaultRef) -> IProtectedDefaultRef where Self: Sized {
-    std::mem::replace(&mut *DEFAULT_IMPL.lock().unwrap(), d)
-  }
-}
-pub trait IProtectedAsync<P>: binder::Interface + Send {
-  fn get_descriptor() -> &'static str where Self: Sized { "android.aidl.tests.permission.IProtected" }
-  fn PermissionProtected<'a>(&'a self) -> binder::BoxFuture<'a, binder::Result<()>>;
-  fn MultiplePermissionsAll<'a>(&'a self) -> binder::BoxFuture<'a, binder::Result<()>>;
-  fn MultiplePermissionsAny<'a>(&'a self) -> binder::BoxFuture<'a, binder::Result<()>>;
-}
-#[::async_trait::async_trait]
-pub trait IProtectedAsyncServer: binder::Interface + Send {
-  fn get_descriptor() -> &'static str where Self: Sized { "android.aidl.tests.permission.IProtected" }
-  async fn PermissionProtected(&self) -> binder::Result<()>;
-  async fn MultiplePermissionsAll(&self) -> binder::Result<()>;
-  async fn MultiplePermissionsAny(&self) -> binder::Result<()>;
-}
-impl BnProtected {
-  /// Create a new async binder service.
-  pub fn new_async_binder<T, R>(inner: T, rt: R, features: binder::BinderFeatures) -> binder::Strong<dyn IProtected>
-  where
-    T: IProtectedAsyncServer + binder::Interface + Send + Sync + 'static,
-    R: binder::binder_impl::BinderAsyncRuntime + Send + Sync + 'static,
-  {
-    struct Wrapper<T, R> {
-      _inner: T,
-      _rt: R,
-    }
-    impl<T, R> binder::Interface for Wrapper<T, R> where T: binder::Interface, R: Send + Sync {
-      fn as_binder(&self) -> binder::SpIBinder { self._inner.as_binder() }
-      fn dump(&self, _file: &std::fs::File, _args: &[&std::ffi::CStr]) -> std::result::Result<(), binder::StatusCode> { self._inner.dump(_file, _args) }
-    }
-    impl<T, R> IProtected for Wrapper<T, R>
-    where
-      T: IProtectedAsyncServer + Send + Sync + 'static,
-      R: binder::binder_impl::BinderAsyncRuntime + Send + Sync + 'static,
-    {
-      fn PermissionProtected(&self) -> binder::Result<()> {
-        self._rt.block_on(self._inner.PermissionProtected())
-      }
-      fn MultiplePermissionsAll(&self) -> binder::Result<()> {
-        self._rt.block_on(self._inner.MultiplePermissionsAll())
-      }
-      fn MultiplePermissionsAny(&self) -> binder::Result<()> {
-        self._rt.block_on(self._inner.MultiplePermissionsAny())
-      }
-    }
-    let wrapped = Wrapper { _inner: inner, _rt: rt };
-    Self::new_binder(wrapped, features)
-  }
-}
-pub trait IProtectedDefault: Send + Sync {
-  fn PermissionProtected(&self) -> binder::Result<()> {
-    Err(binder::StatusCode::UNKNOWN_TRANSACTION.into())
-  }
-  fn MultiplePermissionsAll(&self) -> binder::Result<()> {
-    Err(binder::StatusCode::UNKNOWN_TRANSACTION.into())
-  }
-  fn MultiplePermissionsAny(&self) -> binder::Result<()> {
-    Err(binder::StatusCode::UNKNOWN_TRANSACTION.into())
-  }
-}
-pub mod transactions {
-  pub const PermissionProtected: binder::binder_impl::TransactionCode = binder::binder_impl::FIRST_CALL_TRANSACTION + 0;
-  pub const MultiplePermissionsAll: binder::binder_impl::TransactionCode = binder::binder_impl::FIRST_CALL_TRANSACTION + 1;
-  pub const MultiplePermissionsAny: binder::binder_impl::TransactionCode = binder::binder_impl::FIRST_CALL_TRANSACTION + 2;
-}
-pub type IProtectedDefaultRef = Option<std::sync::Arc<dyn IProtectedDefault>>;
-use lazy_static::lazy_static;
-lazy_static! {
-  static ref DEFAULT_IMPL: std::sync::Mutex<IProtectedDefaultRef> = std::sync::Mutex::new(None);
-}
-impl BpProtected {
-  fn build_parcel_PermissionProtected(&self) -> binder::Result<binder::binder_impl::Parcel> {
-    let mut aidl_data = self.binder.prepare_transact()?;
-    Ok(aidl_data)
-  }
-  fn read_response_PermissionProtected(&self, _aidl_reply: std::result::Result<binder::binder_impl::Parcel, binder::StatusCode>) -> binder::Result<()> {
-    if let Err(binder::StatusCode::UNKNOWN_TRANSACTION) = _aidl_reply {
-      if let Some(_aidl_default_impl) = <Self as IProtected>::getDefaultImpl() {
-        return _aidl_default_impl.PermissionProtected();
-      }
-    }
-    let _aidl_reply = _aidl_reply?;
-    let _aidl_status: binder::Status = _aidl_reply.read()?;
-    if !_aidl_status.is_ok() { return Err(_aidl_status); }
-    Ok(())
-  }
-  fn build_parcel_MultiplePermissionsAll(&self) -> binder::Result<binder::binder_impl::Parcel> {
-    let mut aidl_data = self.binder.prepare_transact()?;
-    Ok(aidl_data)
-  }
-  fn read_response_MultiplePermissionsAll(&self, _aidl_reply: std::result::Result<binder::binder_impl::Parcel, binder::StatusCode>) -> binder::Result<()> {
-    if let Err(binder::StatusCode::UNKNOWN_TRANSACTION) = _aidl_reply {
-      if let Some(_aidl_default_impl) = <Self as IProtected>::getDefaultImpl() {
-        return _aidl_default_impl.MultiplePermissionsAll();
-      }
-    }
-    let _aidl_reply = _aidl_reply?;
-    let _aidl_status: binder::Status = _aidl_reply.read()?;
-    if !_aidl_status.is_ok() { return Err(_aidl_status); }
-    Ok(())
-  }
-  fn build_parcel_MultiplePermissionsAny(&self) -> binder::Result<binder::binder_impl::Parcel> {
-    let mut aidl_data = self.binder.prepare_transact()?;
-    Ok(aidl_data)
-  }
-  fn read_response_MultiplePermissionsAny(&self, _aidl_reply: std::result::Result<binder::binder_impl::Parcel, binder::StatusCode>) -> binder::Result<()> {
-    if let Err(binder::StatusCode::UNKNOWN_TRANSACTION) = _aidl_reply {
-      if let Some(_aidl_default_impl) = <Self as IProtected>::getDefaultImpl() {
-        return _aidl_default_impl.MultiplePermissionsAny();
-      }
-    }
-    let _aidl_reply = _aidl_reply?;
-    let _aidl_status: binder::Status = _aidl_reply.read()?;
-    if !_aidl_status.is_ok() { return Err(_aidl_status); }
-    Ok(())
-  }
-}
-impl IProtected for BpProtected {
-  fn PermissionProtected(&self) -> binder::Result<()> {
-    let _aidl_data = self.build_parcel_PermissionProtected()?;
-    let _aidl_reply = self.binder.submit_transact(transactions::PermissionProtected, _aidl_data, binder::binder_impl::FLAG_PRIVATE_LOCAL);
-    self.read_response_PermissionProtected(_aidl_reply)
-  }
-  fn MultiplePermissionsAll(&self) -> binder::Result<()> {
-    let _aidl_data = self.build_parcel_MultiplePermissionsAll()?;
-    let _aidl_reply = self.binder.submit_transact(transactions::MultiplePermissionsAll, _aidl_data, binder::binder_impl::FLAG_PRIVATE_LOCAL);
-    self.read_response_MultiplePermissionsAll(_aidl_reply)
-  }
-  fn MultiplePermissionsAny(&self) -> binder::Result<()> {
-    let _aidl_data = self.build_parcel_MultiplePermissionsAny()?;
-    let _aidl_reply = self.binder.submit_transact(transactions::MultiplePermissionsAny, _aidl_data, binder::binder_impl::FLAG_PRIVATE_LOCAL);
-    self.read_response_MultiplePermissionsAny(_aidl_reply)
-  }
-}
-impl<P: binder::BinderAsyncPool> IProtectedAsync<P> for BpProtected {
-  fn PermissionProtected<'a>(&'a self) -> binder::BoxFuture<'a, binder::Result<()>> {
-    let _aidl_data = match self.build_parcel_PermissionProtected() {
-      Ok(_aidl_data) => _aidl_data,
-      Err(err) => return Box::pin(std::future::ready(Err(err))),
-    };
-    let binder = self.binder.clone();
-    P::spawn(
-      move || binder.submit_transact(transactions::PermissionProtected, _aidl_data, binder::binder_impl::FLAG_PRIVATE_LOCAL),
-      move |_aidl_reply| async move {
-        self.read_response_PermissionProtected(_aidl_reply)
-      }
-    )
-  }
-  fn MultiplePermissionsAll<'a>(&'a self) -> binder::BoxFuture<'a, binder::Result<()>> {
-    let _aidl_data = match self.build_parcel_MultiplePermissionsAll() {
-      Ok(_aidl_data) => _aidl_data,
-      Err(err) => return Box::pin(std::future::ready(Err(err))),
-    };
-    let binder = self.binder.clone();
-    P::spawn(
-      move || binder.submit_transact(transactions::MultiplePermissionsAll, _aidl_data, binder::binder_impl::FLAG_PRIVATE_LOCAL),
-      move |_aidl_reply| async move {
-        self.read_response_MultiplePermissionsAll(_aidl_reply)
-      }
-    )
-  }
-  fn MultiplePermissionsAny<'a>(&'a self) -> binder::BoxFuture<'a, binder::Result<()>> {
-    let _aidl_data = match self.build_parcel_MultiplePermissionsAny() {
-      Ok(_aidl_data) => _aidl_data,
-      Err(err) => return Box::pin(std::future::ready(Err(err))),
-    };
-    let binder = self.binder.clone();
-    P::spawn(
-      move || binder.submit_transact(transactions::MultiplePermissionsAny, _aidl_data, binder::binder_impl::FLAG_PRIVATE_LOCAL),
-      move |_aidl_reply| async move {
-        self.read_response_MultiplePermissionsAny(_aidl_reply)
-      }
-    )
-  }
-}
-impl IProtected for binder::binder_impl::Binder<BnProtected> {
-  fn PermissionProtected(&self) -> binder::Result<()> { self.0.PermissionProtected() }
-  fn MultiplePermissionsAll(&self) -> binder::Result<()> { self.0.MultiplePermissionsAll() }
-  fn MultiplePermissionsAny(&self) -> binder::Result<()> { self.0.MultiplePermissionsAny() }
-}
-fn on_transact(_aidl_service: &dyn IProtected, _aidl_code: binder::binder_impl::TransactionCode, _aidl_data: &binder::binder_impl::BorrowedParcel<'_>, _aidl_reply: &mut binder::binder_impl::BorrowedParcel<'_>) -> std::result::Result<(), binder::StatusCode> {
-  match _aidl_code {
-    transactions::PermissionProtected => {
-      let _aidl_return = _aidl_service.PermissionProtected();
-      match &_aidl_return {
-        Ok(_aidl_return) => {
-          _aidl_reply.write(&binder::Status::from(binder::StatusCode::OK))?;
-        }
-        Err(_aidl_status) => _aidl_reply.write(_aidl_status)?
-      }
-      Ok(())
-    }
-    transactions::MultiplePermissionsAll => {
-      let _aidl_return = _aidl_service.MultiplePermissionsAll();
-      match &_aidl_return {
-        Ok(_aidl_return) => {
-          _aidl_reply.write(&binder::Status::from(binder::StatusCode::OK))?;
-        }
-        Err(_aidl_status) => _aidl_reply.write(_aidl_status)?
-      }
-      Ok(())
-    }
-    transactions::MultiplePermissionsAny => {
-      let _aidl_return = _aidl_service.MultiplePermissionsAny();
-      match &_aidl_return {
-        Ok(_aidl_return) => {
-          _aidl_reply.write(&binder::Status::from(binder::StatusCode::OK))?;
-        }
-        Err(_aidl_status) => _aidl_reply.write(_aidl_status)?
-      }
-      Ok(())
-    }
-    _ => Err(binder::StatusCode::UNKNOWN_TRANSACTION)
-  }
-}
-pub(crate) mod mangled {
- pub use super::IProtected as _7_android_4_aidl_5_tests_10_permission_10_IProtected;
-}
diff --git a/tests/golden_output/aidl-test-interface-rust-source/gen/android/aidl/tests/permission/IProtected.rs.d b/tests/golden_output/aidl-test-interface-rust-source/gen/android/aidl/tests/permission/IProtected.rs.d
deleted file mode 100644
index 72476b7..0000000
--- a/tests/golden_output/aidl-test-interface-rust-source/gen/android/aidl/tests/permission/IProtected.rs.d
+++ /dev/null
@@ -1,2 +0,0 @@
-out/soong/.intermediates/system/tools/aidl/aidl-test-interface-rust-source/gen/android/aidl/tests/permission/IProtected.rs : \
-  system/tools/aidl/tests/android/aidl/tests/permission/IProtected.aidl
diff --git a/tests/golden_output/aidl-test-interface-rust-source/gen/android/aidl/tests/permission/IProtectedInterface.rs b/tests/golden_output/aidl-test-interface-rust-source/gen/android/aidl/tests/permission/IProtectedInterface.rs
deleted file mode 100644
index cdbf3b5..0000000
--- a/tests/golden_output/aidl-test-interface-rust-source/gen/android/aidl/tests/permission/IProtectedInterface.rs
+++ /dev/null
@@ -1,188 +0,0 @@
-#![forbid(unsafe_code)]
-#![rustfmt::skip]
-#![allow(non_upper_case_globals)]
-#![allow(non_snake_case)]
-#[allow(unused_imports)] use binder::binder_impl::IBinderInternal;
-use binder::declare_binder_interface;
-declare_binder_interface! {
-  IProtectedInterface["android.aidl.tests.permission.IProtectedInterface"] {
-    native: BnProtectedInterface(on_transact),
-    proxy: BpProtectedInterface {
-    },
-    async: IProtectedInterfaceAsync,
-  }
-}
-pub trait IProtectedInterface: binder::Interface + Send {
-  fn get_descriptor() -> &'static str where Self: Sized { "android.aidl.tests.permission.IProtectedInterface" }
-  fn Method1(&self) -> binder::Result<()>;
-  fn Method2(&self) -> binder::Result<()>;
-  fn getDefaultImpl() -> IProtectedInterfaceDefaultRef where Self: Sized {
-    DEFAULT_IMPL.lock().unwrap().clone()
-  }
-  fn setDefaultImpl(d: IProtectedInterfaceDefaultRef) -> IProtectedInterfaceDefaultRef where Self: Sized {
-    std::mem::replace(&mut *DEFAULT_IMPL.lock().unwrap(), d)
-  }
-}
-pub trait IProtectedInterfaceAsync<P>: binder::Interface + Send {
-  fn get_descriptor() -> &'static str where Self: Sized { "android.aidl.tests.permission.IProtectedInterface" }
-  fn Method1<'a>(&'a self) -> binder::BoxFuture<'a, binder::Result<()>>;
-  fn Method2<'a>(&'a self) -> binder::BoxFuture<'a, binder::Result<()>>;
-}
-#[::async_trait::async_trait]
-pub trait IProtectedInterfaceAsyncServer: binder::Interface + Send {
-  fn get_descriptor() -> &'static str where Self: Sized { "android.aidl.tests.permission.IProtectedInterface" }
-  async fn Method1(&self) -> binder::Result<()>;
-  async fn Method2(&self) -> binder::Result<()>;
-}
-impl BnProtectedInterface {
-  /// Create a new async binder service.
-  pub fn new_async_binder<T, R>(inner: T, rt: R, features: binder::BinderFeatures) -> binder::Strong<dyn IProtectedInterface>
-  where
-    T: IProtectedInterfaceAsyncServer + binder::Interface + Send + Sync + 'static,
-    R: binder::binder_impl::BinderAsyncRuntime + Send + Sync + 'static,
-  {
-    struct Wrapper<T, R> {
-      _inner: T,
-      _rt: R,
-    }
-    impl<T, R> binder::Interface for Wrapper<T, R> where T: binder::Interface, R: Send + Sync {
-      fn as_binder(&self) -> binder::SpIBinder { self._inner.as_binder() }
-      fn dump(&self, _file: &std::fs::File, _args: &[&std::ffi::CStr]) -> std::result::Result<(), binder::StatusCode> { self._inner.dump(_file, _args) }
-    }
-    impl<T, R> IProtectedInterface for Wrapper<T, R>
-    where
-      T: IProtectedInterfaceAsyncServer + Send + Sync + 'static,
-      R: binder::binder_impl::BinderAsyncRuntime + Send + Sync + 'static,
-    {
-      fn Method1(&self) -> binder::Result<()> {
-        self._rt.block_on(self._inner.Method1())
-      }
-      fn Method2(&self) -> binder::Result<()> {
-        self._rt.block_on(self._inner.Method2())
-      }
-    }
-    let wrapped = Wrapper { _inner: inner, _rt: rt };
-    Self::new_binder(wrapped, features)
-  }
-}
-pub trait IProtectedInterfaceDefault: Send + Sync {
-  fn Method1(&self) -> binder::Result<()> {
-    Err(binder::StatusCode::UNKNOWN_TRANSACTION.into())
-  }
-  fn Method2(&self) -> binder::Result<()> {
-    Err(binder::StatusCode::UNKNOWN_TRANSACTION.into())
-  }
-}
-pub mod transactions {
-  pub const Method1: binder::binder_impl::TransactionCode = binder::binder_impl::FIRST_CALL_TRANSACTION + 0;
-  pub const Method2: binder::binder_impl::TransactionCode = binder::binder_impl::FIRST_CALL_TRANSACTION + 1;
-}
-pub type IProtectedInterfaceDefaultRef = Option<std::sync::Arc<dyn IProtectedInterfaceDefault>>;
-use lazy_static::lazy_static;
-lazy_static! {
-  static ref DEFAULT_IMPL: std::sync::Mutex<IProtectedInterfaceDefaultRef> = std::sync::Mutex::new(None);
-}
-impl BpProtectedInterface {
-  fn build_parcel_Method1(&self) -> binder::Result<binder::binder_impl::Parcel> {
-    let mut aidl_data = self.binder.prepare_transact()?;
-    Ok(aidl_data)
-  }
-  fn read_response_Method1(&self, _aidl_reply: std::result::Result<binder::binder_impl::Parcel, binder::StatusCode>) -> binder::Result<()> {
-    if let Err(binder::StatusCode::UNKNOWN_TRANSACTION) = _aidl_reply {
-      if let Some(_aidl_default_impl) = <Self as IProtectedInterface>::getDefaultImpl() {
-        return _aidl_default_impl.Method1();
-      }
-    }
-    let _aidl_reply = _aidl_reply?;
-    let _aidl_status: binder::Status = _aidl_reply.read()?;
-    if !_aidl_status.is_ok() { return Err(_aidl_status); }
-    Ok(())
-  }
-  fn build_parcel_Method2(&self) -> binder::Result<binder::binder_impl::Parcel> {
-    let mut aidl_data = self.binder.prepare_transact()?;
-    Ok(aidl_data)
-  }
-  fn read_response_Method2(&self, _aidl_reply: std::result::Result<binder::binder_impl::Parcel, binder::StatusCode>) -> binder::Result<()> {
-    if let Err(binder::StatusCode::UNKNOWN_TRANSACTION) = _aidl_reply {
-      if let Some(_aidl_default_impl) = <Self as IProtectedInterface>::getDefaultImpl() {
-        return _aidl_default_impl.Method2();
-      }
-    }
-    let _aidl_reply = _aidl_reply?;
-    let _aidl_status: binder::Status = _aidl_reply.read()?;
-    if !_aidl_status.is_ok() { return Err(_aidl_status); }
-    Ok(())
-  }
-}
-impl IProtectedInterface for BpProtectedInterface {
-  fn Method1(&self) -> binder::Result<()> {
-    let _aidl_data = self.build_parcel_Method1()?;
-    let _aidl_reply = self.binder.submit_transact(transactions::Method1, _aidl_data, binder::binder_impl::FLAG_PRIVATE_LOCAL);
-    self.read_response_Method1(_aidl_reply)
-  }
-  fn Method2(&self) -> binder::Result<()> {
-    let _aidl_data = self.build_parcel_Method2()?;
-    let _aidl_reply = self.binder.submit_transact(transactions::Method2, _aidl_data, binder::binder_impl::FLAG_PRIVATE_LOCAL);
-    self.read_response_Method2(_aidl_reply)
-  }
-}
-impl<P: binder::BinderAsyncPool> IProtectedInterfaceAsync<P> for BpProtectedInterface {
-  fn Method1<'a>(&'a self) -> binder::BoxFuture<'a, binder::Result<()>> {
-    let _aidl_data = match self.build_parcel_Method1() {
-      Ok(_aidl_data) => _aidl_data,
-      Err(err) => return Box::pin(std::future::ready(Err(err))),
-    };
-    let binder = self.binder.clone();
-    P::spawn(
-      move || binder.submit_transact(transactions::Method1, _aidl_data, binder::binder_impl::FLAG_PRIVATE_LOCAL),
-      move |_aidl_reply| async move {
-        self.read_response_Method1(_aidl_reply)
-      }
-    )
-  }
-  fn Method2<'a>(&'a self) -> binder::BoxFuture<'a, binder::Result<()>> {
-    let _aidl_data = match self.build_parcel_Method2() {
-      Ok(_aidl_data) => _aidl_data,
-      Err(err) => return Box::pin(std::future::ready(Err(err))),
-    };
-    let binder = self.binder.clone();
-    P::spawn(
-      move || binder.submit_transact(transactions::Method2, _aidl_data, binder::binder_impl::FLAG_PRIVATE_LOCAL),
-      move |_aidl_reply| async move {
-        self.read_response_Method2(_aidl_reply)
-      }
-    )
-  }
-}
-impl IProtectedInterface for binder::binder_impl::Binder<BnProtectedInterface> {
-  fn Method1(&self) -> binder::Result<()> { self.0.Method1() }
-  fn Method2(&self) -> binder::Result<()> { self.0.Method2() }
-}
-fn on_transact(_aidl_service: &dyn IProtectedInterface, _aidl_code: binder::binder_impl::TransactionCode, _aidl_data: &binder::binder_impl::BorrowedParcel<'_>, _aidl_reply: &mut binder::binder_impl::BorrowedParcel<'_>) -> std::result::Result<(), binder::StatusCode> {
-  match _aidl_code {
-    transactions::Method1 => {
-      let _aidl_return = _aidl_service.Method1();
-      match &_aidl_return {
-        Ok(_aidl_return) => {
-          _aidl_reply.write(&binder::Status::from(binder::StatusCode::OK))?;
-        }
-        Err(_aidl_status) => _aidl_reply.write(_aidl_status)?
-      }
-      Ok(())
-    }
-    transactions::Method2 => {
-      let _aidl_return = _aidl_service.Method2();
-      match &_aidl_return {
-        Ok(_aidl_return) => {
-          _aidl_reply.write(&binder::Status::from(binder::StatusCode::OK))?;
-        }
-        Err(_aidl_status) => _aidl_reply.write(_aidl_status)?
-      }
-      Ok(())
-    }
-    _ => Err(binder::StatusCode::UNKNOWN_TRANSACTION)
-  }
-}
-pub(crate) mod mangled {
- pub use super::IProtectedInterface as _7_android_4_aidl_5_tests_10_permission_19_IProtectedInterface;
-}
diff --git a/tests/golden_output/aidl-test-interface-rust-source/gen/android/aidl/tests/permission/IProtectedInterface.rs.d b/tests/golden_output/aidl-test-interface-rust-source/gen/android/aidl/tests/permission/IProtectedInterface.rs.d
deleted file mode 100644
index 318359f..0000000
--- a/tests/golden_output/aidl-test-interface-rust-source/gen/android/aidl/tests/permission/IProtectedInterface.rs.d
+++ /dev/null
@@ -1,2 +0,0 @@
-out/soong/.intermediates/system/tools/aidl/aidl-test-interface-rust-source/gen/android/aidl/tests/permission/IProtectedInterface.rs : \
-  system/tools/aidl/tests/android/aidl/tests/permission/IProtectedInterface.aidl
diff --git a/tests/golden_output/aidl-test-interface-rust-source/gen/android/aidl/tests/unions/EnumUnion.rs b/tests/golden_output/aidl-test-interface-rust-source/gen/android/aidl/tests/unions/EnumUnion.rs
index bf142f7..6f26dcc 100644
--- a/tests/golden_output/aidl-test-interface-rust-source/gen/android/aidl/tests/unions/EnumUnion.rs
+++ b/tests/golden_output/aidl-test-interface-rust-source/gen/android/aidl/tests/unions/EnumUnion.rs
@@ -4,6 +4,8 @@
 pub enum EnumUnion {
   IntEnum(crate::mangled::_7_android_4_aidl_5_tests_7_IntEnum),
   LongEnum(crate::mangled::_7_android_4_aidl_5_tests_8_LongEnum),
+  #[deprecated = "do not use this"]
+  DeprecatedField(i32),
 }
 impl Default for EnumUnion {
   fn default() -> Self {
@@ -21,6 +23,10 @@
         parcel.write(&1i32)?;
         parcel.write(v)
       }
+      Self::DeprecatedField(v) => {
+        parcel.write(&2i32)?;
+        parcel.write(v)
+      }
     }
   }
   fn read_from_parcel(&mut self, parcel: &binder::binder_impl::BorrowedParcel) -> std::result::Result<(), binder::StatusCode> {
@@ -36,6 +42,11 @@
         *self = Self::LongEnum(value);
         Ok(())
       }
+      2 => {
+        let value: i32 = parcel.read()?;
+        *self = Self::DeprecatedField(value);
+        Ok(())
+      }
       _ => {
         Err(binder::StatusCode::BAD_VALUE)
       }
@@ -47,6 +58,19 @@
 impl binder::binder_impl::ParcelableMetadata for EnumUnion {
   fn get_descriptor() -> &'static str { "android.aidl.tests.unions.EnumUnion" }
 }
+pub mod Tag {
+  #![allow(non_upper_case_globals)]
+  use binder::declare_binder_enum;
+  declare_binder_enum! {
+    Tag : [i32; 3] {
+      intEnum = 0,
+      longEnum = 1,
+      #[deprecated = "do not use this"]
+      deprecatedField = 2,
+    }
+  }
+}
 pub(crate) mod mangled {
  pub use super::EnumUnion as _7_android_4_aidl_5_tests_6_unions_9_EnumUnion;
+ pub use super::Tag::Tag as _7_android_4_aidl_5_tests_6_unions_9_EnumUnion_3_Tag;
 }
diff --git a/tests/golden_output/aidl-test-interface-rust-source/gen/android/aidl/tests/unions/UnionInUnion.rs b/tests/golden_output/aidl-test-interface-rust-source/gen/android/aidl/tests/unions/UnionInUnion.rs
index fd2d68d..511f44c 100644
--- a/tests/golden_output/aidl-test-interface-rust-source/gen/android/aidl/tests/unions/UnionInUnion.rs
+++ b/tests/golden_output/aidl-test-interface-rust-source/gen/android/aidl/tests/unions/UnionInUnion.rs
@@ -47,6 +47,17 @@
 impl binder::binder_impl::ParcelableMetadata for UnionInUnion {
   fn get_descriptor() -> &'static str { "android.aidl.tests.unions.UnionInUnion" }
 }
+pub mod Tag {
+  #![allow(non_upper_case_globals)]
+  use binder::declare_binder_enum;
+  declare_binder_enum! {
+    Tag : [i32; 2] {
+      first = 0,
+      second = 1,
+    }
+  }
+}
 pub(crate) mod mangled {
  pub use super::UnionInUnion as _7_android_4_aidl_5_tests_6_unions_12_UnionInUnion;
+ pub use super::Tag::Tag as _7_android_4_aidl_5_tests_6_unions_12_UnionInUnion_3_Tag;
 }
diff --git a/tests/golden_output/aidl-test-versioned-interface-V1-cpp-source/gen/android/aidl/versioned/tests/BazUnion.cpp b/tests/golden_output/aidl-test-versioned-interface-V1-cpp-source/gen/android/aidl/versioned/tests/BazUnion.cpp
index 68dc580..eb66154 100644
--- a/tests/golden_output/aidl-test-versioned-interface-V1-cpp-source/gen/android/aidl/versioned/tests/BazUnion.cpp
+++ b/tests/golden_output/aidl-test-versioned-interface-V1-cpp-source/gen/android/aidl/versioned/tests/BazUnion.cpp
@@ -8,7 +8,7 @@
   ::android::status_t _aidl_ret_status;
   int32_t _aidl_tag;
   if ((_aidl_ret_status = _aidl_parcel->readInt32(&_aidl_tag)) != ::android::OK) return _aidl_ret_status;
-  switch (_aidl_tag) {
+  switch (static_cast<Tag>(_aidl_tag)) {
   case intNum: {
     int32_t _aidl_value;
     if ((_aidl_ret_status = _aidl_parcel->readInt32(&_aidl_value)) != ::android::OK) return _aidl_ret_status;
@@ -23,7 +23,7 @@
   return ::android::BAD_VALUE;
 }
 ::android::status_t BazUnion::writeToParcel(::android::Parcel* _aidl_parcel) const {
-  ::android::status_t _aidl_ret_status = _aidl_parcel->writeInt32(getTag());
+  ::android::status_t _aidl_ret_status = _aidl_parcel->writeInt32(static_cast<int32_t>(getTag()));
   if (_aidl_ret_status != ::android::OK) return _aidl_ret_status;
   switch (getTag()) {
   case intNum: return _aidl_parcel->writeInt32(get<intNum>());
diff --git a/tests/golden_output/aidl-test-versioned-interface-V1-cpp-source/gen/include/android/aidl/versioned/tests/BazUnion.h b/tests/golden_output/aidl-test-versioned-interface-V1-cpp-source/gen/include/android/aidl/versioned/tests/BazUnion.h
index f28108f..25e0d16 100644
--- a/tests/golden_output/aidl-test-versioned-interface-V1-cpp-source/gen/include/android/aidl/versioned/tests/BazUnion.h
+++ b/tests/golden_output/aidl-test-versioned-interface-V1-cpp-source/gen/include/android/aidl/versioned/tests/BazUnion.h
@@ -1,10 +1,13 @@
 #pragma once
 
 #include <android/binder_to_string.h>
+#include <array>
+#include <binder/Enums.h>
 #include <binder/Parcel.h>
 #include <binder/Status.h>
 #include <cassert>
 #include <cstdint>
+#include <string>
 #include <type_traits>
 #include <utility>
 #include <utils/String16.h>
@@ -20,14 +23,16 @@
 namespace tests {
 class BazUnion : public ::android::Parcelable {
 public:
-  enum Tag : int32_t {
-    intNum = 0,  // int intNum;
+  enum class Tag : int32_t {
+    intNum = 0,
   };
+  // Expose tag symbols for legacy code
+  static const inline Tag intNum = Tag::intNum;
 
   template<typename _Tp>
   static constexpr bool _not_self = !std::is_same_v<std::remove_cv_t<std::remove_reference_t<_Tp>>, BazUnion>;
 
-  BazUnion() : _value(std::in_place_index<intNum>, int32_t(0)) { }
+  BazUnion() : _value(std::in_place_index<static_cast<size_t>(intNum)>, int32_t(0)) { }
 
   template <typename _Tp, typename = std::enable_if_t<_not_self<_Tp>>>
   // NOLINTNEXTLINE(google-explicit-constructor)
@@ -40,12 +45,12 @@
 
   template <Tag _tag, typename... _Tp>
   static BazUnion make(_Tp&&... _args) {
-    return BazUnion(std::in_place_index<_tag>, std::forward<_Tp>(_args)...);
+    return BazUnion(std::in_place_index<static_cast<size_t>(_tag)>, std::forward<_Tp>(_args)...);
   }
 
   template <Tag _tag, typename _Tp, typename... _Up>
   static BazUnion make(std::initializer_list<_Tp> _il, _Up&&... _args) {
-    return BazUnion(std::in_place_index<_tag>, std::move(_il), std::forward<_Up>(_args)...);
+    return BazUnion(std::in_place_index<static_cast<size_t>(_tag)>, std::move(_il), std::forward<_Up>(_args)...);
   }
 
   Tag getTag() const {
@@ -55,18 +60,18 @@
   template <Tag _tag>
   const auto& get() const {
     if (getTag() != _tag) { __assert2(__FILE__, __LINE__, __PRETTY_FUNCTION__, "bad access: a wrong tag"); }
-    return std::get<_tag>(_value);
+    return std::get<static_cast<size_t>(_tag)>(_value);
   }
 
   template <Tag _tag>
   auto& get() {
     if (getTag() != _tag) { __assert2(__FILE__, __LINE__, __PRETTY_FUNCTION__, "bad access: a wrong tag"); }
-    return std::get<_tag>(_value);
+    return std::get<static_cast<size_t>(_tag)>(_value);
   }
 
   template <Tag _tag, typename... _Tp>
   void set(_Tp&&... _args) {
-    _value.emplace<_tag>(std::forward<_Tp>(_args)...);
+    _value.emplace<static_cast<size_t>(_tag)>(std::forward<_Tp>(_args)...);
   }
 
   inline bool operator!=(const BazUnion& rhs) const {
@@ -110,3 +115,30 @@
 }  // namespace versioned
 }  // namespace aidl
 }  // namespace android
+namespace android {
+namespace aidl {
+namespace versioned {
+namespace tests {
+[[nodiscard]] static inline std::string toString(BazUnion::Tag val) {
+  switch(val) {
+  case BazUnion::Tag::intNum:
+    return "intNum";
+  default:
+    return std::to_string(static_cast<int32_t>(val));
+  }
+}
+}  // namespace tests
+}  // namespace versioned
+}  // namespace aidl
+}  // namespace android
+namespace android {
+namespace internal {
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wc++17-extensions"
+template <>
+constexpr inline std::array<::android::aidl::versioned::tests::BazUnion::Tag, 1> enum_values<::android::aidl::versioned::tests::BazUnion::Tag> = {
+  ::android::aidl::versioned::tests::BazUnion::Tag::intNum,
+};
+#pragma clang diagnostic pop
+}  // namespace internal
+}  // namespace android
diff --git a/tests/golden_output/aidl-test-versioned-interface-V1-java-source/gen/android/aidl/versioned/tests/BazUnion.java b/tests/golden_output/aidl-test-versioned-interface-V1-java-source/gen/android/aidl/versioned/tests/BazUnion.java
index 29da9c2..3f0e3d7 100644
--- a/tests/golden_output/aidl-test-versioned-interface-V1-java-source/gen/android/aidl/versioned/tests/BazUnion.java
+++ b/tests/golden_output/aidl-test-versioned-interface-V1-java-source/gen/android/aidl/versioned/tests/BazUnion.java
@@ -102,4 +102,7 @@
     this._tag = _tag;
     this._value = _value;
   }
+  public static @interface Tag {
+    public static final int intNum = 0;
+  }
 }
diff --git a/tests/golden_output/aidl-test-versioned-interface-V1-ndk-source/gen/android/aidl/versioned/tests/BazUnion.cpp b/tests/golden_output/aidl-test-versioned-interface-V1-ndk-source/gen/android/aidl/versioned/tests/BazUnion.cpp
index 13b3467..118a85e 100644
--- a/tests/golden_output/aidl-test-versioned-interface-V1-ndk-source/gen/android/aidl/versioned/tests/BazUnion.cpp
+++ b/tests/golden_output/aidl-test-versioned-interface-V1-ndk-source/gen/android/aidl/versioned/tests/BazUnion.cpp
@@ -13,7 +13,7 @@
   binder_status_t _aidl_ret_status;
   int32_t _aidl_tag;
   if ((_aidl_ret_status = ::ndk::AParcel_readData(_parcel, &_aidl_tag)) != STATUS_OK) return _aidl_ret_status;
-  switch (_aidl_tag) {
+  switch (static_cast<Tag>(_aidl_tag)) {
   case intNum: {
     int32_t _aidl_value;
     if ((_aidl_ret_status = ::ndk::AParcel_readData(_parcel, &_aidl_value)) != STATUS_OK) return _aidl_ret_status;
@@ -28,7 +28,7 @@
   return STATUS_BAD_VALUE;
 }
 binder_status_t BazUnion::writeToParcel(AParcel* _parcel) const {
-  binder_status_t _aidl_ret_status = ::ndk::AParcel_writeData(_parcel, getTag());
+  binder_status_t _aidl_ret_status = ::ndk::AParcel_writeData(_parcel, static_cast<int32_t>(getTag()));
   if (_aidl_ret_status != STATUS_OK) return _aidl_ret_status;
   switch (getTag()) {
   case intNum: return ::ndk::AParcel_writeData(_parcel, get<intNum>());
diff --git a/tests/golden_output/aidl-test-versioned-interface-V1-ndk-source/gen/include/aidl/android/aidl/versioned/tests/BazUnion.h b/tests/golden_output/aidl-test-versioned-interface-V1-ndk-source/gen/include/aidl/android/aidl/versioned/tests/BazUnion.h
index 3cbd032..9218a0b 100644
--- a/tests/golden_output/aidl-test-versioned-interface-V1-ndk-source/gen/include/aidl/android/aidl/versioned/tests/BazUnion.h
+++ b/tests/golden_output/aidl-test-versioned-interface-V1-ndk-source/gen/include/aidl/android/aidl/versioned/tests/BazUnion.h
@@ -1,5 +1,6 @@
 #pragma once
 
+#include <array>
 #include <cassert>
 #include <cstdint>
 #include <memory>
@@ -9,6 +10,7 @@
 #include <utility>
 #include <variant>
 #include <vector>
+#include <android/binder_enums.h>
 #include <android/binder_interface_utils.h>
 #include <android/binder_parcelable_utils.h>
 #include <android/binder_to_string.h>
@@ -30,14 +32,17 @@
   typedef std::false_type fixed_size;
   static const char* descriptor;
 
-  enum Tag : int32_t {
-    intNum = 0,  // int intNum;
+  enum class Tag : int32_t {
+    intNum = 0,
   };
 
+  // Expose tag symbols for legacy code
+  static const inline Tag intNum = Tag::intNum;
+
   template<typename _Tp>
   static constexpr bool _not_self = !std::is_same_v<std::remove_cv_t<std::remove_reference_t<_Tp>>, BazUnion>;
 
-  BazUnion() : _value(std::in_place_index<intNum>, int32_t(0)) { }
+  BazUnion() : _value(std::in_place_index<static_cast<size_t>(intNum)>, int32_t(0)) { }
 
   template <typename _Tp, typename = std::enable_if_t<_not_self<_Tp>>>
   // NOLINTNEXTLINE(google-explicit-constructor)
@@ -50,12 +55,12 @@
 
   template <Tag _tag, typename... _Tp>
   static BazUnion make(_Tp&&... _args) {
-    return BazUnion(std::in_place_index<_tag>, std::forward<_Tp>(_args)...);
+    return BazUnion(std::in_place_index<static_cast<size_t>(_tag)>, std::forward<_Tp>(_args)...);
   }
 
   template <Tag _tag, typename _Tp, typename... _Up>
   static BazUnion make(std::initializer_list<_Tp> _il, _Up&&... _args) {
-    return BazUnion(std::in_place_index<_tag>, std::move(_il), std::forward<_Up>(_args)...);
+    return BazUnion(std::in_place_index<static_cast<size_t>(_tag)>, std::move(_il), std::forward<_Up>(_args)...);
   }
 
   Tag getTag() const {
@@ -65,18 +70,18 @@
   template <Tag _tag>
   const auto& get() const {
     if (getTag() != _tag) { __assert2(__FILE__, __LINE__, __PRETTY_FUNCTION__, "bad access: a wrong tag"); }
-    return std::get<_tag>(_value);
+    return std::get<static_cast<size_t>(_tag)>(_value);
   }
 
   template <Tag _tag>
   auto& get() {
     if (getTag() != _tag) { __assert2(__FILE__, __LINE__, __PRETTY_FUNCTION__, "bad access: a wrong tag"); }
-    return std::get<_tag>(_value);
+    return std::get<static_cast<size_t>(_tag)>(_value);
   }
 
   template <Tag _tag, typename... _Tp>
   void set(_Tp&&... _args) {
-    _value.emplace<_tag>(std::forward<_Tp>(_args)...);
+    _value.emplace<static_cast<size_t>(_tag)>(std::forward<_Tp>(_args)...);
   }
 
   binder_status_t readFromParcel(const AParcel* _parcel);
@@ -119,3 +124,32 @@
 }  // namespace aidl
 }  // namespace android
 }  // namespace aidl
+namespace aidl {
+namespace android {
+namespace aidl {
+namespace versioned {
+namespace tests {
+[[nodiscard]] static inline std::string toString(BazUnion::Tag val) {
+  switch(val) {
+  case BazUnion::Tag::intNum:
+    return "intNum";
+  default:
+    return std::to_string(static_cast<int32_t>(val));
+  }
+}
+}  // namespace tests
+}  // namespace versioned
+}  // namespace aidl
+}  // namespace android
+}  // namespace aidl
+namespace ndk {
+namespace internal {
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wc++17-extensions"
+template <>
+constexpr inline std::array<aidl::android::aidl::versioned::tests::BazUnion::Tag, 1> enum_values<aidl::android::aidl::versioned::tests::BazUnion::Tag> = {
+  aidl::android::aidl::versioned::tests::BazUnion::Tag::intNum,
+};
+#pragma clang diagnostic pop
+}  // namespace internal
+}  // namespace ndk
diff --git a/tests/golden_output/aidl-test-versioned-interface-V1-ndk-source/gen/include/aidl/android/aidl/versioned/tests/BnFooInterface.h b/tests/golden_output/aidl-test-versioned-interface-V1-ndk-source/gen/include/aidl/android/aidl/versioned/tests/BnFooInterface.h
index f464a1e..cdb26d7 100644
--- a/tests/golden_output/aidl-test-versioned-interface-V1-ndk-source/gen/include/aidl/android/aidl/versioned/tests/BnFooInterface.h
+++ b/tests/golden_output/aidl-test-versioned-interface-V1-ndk-source/gen/include/aidl/android/aidl/versioned/tests/BnFooInterface.h
@@ -3,6 +3,13 @@
 #include "aidl/android/aidl/versioned/tests/IFooInterface.h"
 
 #include <android/binder_ibinder.h>
+#include <cassert>
+
+#ifndef __BIONIC__
+#ifndef __assert2
+#define __assert2(a,b,c,d) ((void)0)
+#endif
+#endif
 
 namespace aidl {
 namespace android {
@@ -19,6 +26,35 @@
   ::ndk::SpAIBinder createBinder() override;
 private:
 };
+class IFooInterfaceDelegator : public BnFooInterface {
+public:
+  explicit IFooInterfaceDelegator(const std::shared_ptr<IFooInterface> &impl) : _impl(impl) {
+     int32_t _impl_ver = 0;
+     if (!impl->getInterfaceVersion(&_impl_ver).isOk()) {;
+        __assert2(__FILE__, __LINE__, __PRETTY_FUNCTION__, "Delegator failed to get version of the implementation.");
+     }
+     if (_impl_ver != IFooInterface::version) {
+        __assert2(__FILE__, __LINE__, __PRETTY_FUNCTION__, "Mismatched versions of delegator and implementation is not allowed.");
+     }
+  }
+
+  ::ndk::ScopedAStatus originalApi() override {
+    return _impl->originalApi();
+  }
+  ::ndk::ScopedAStatus acceptUnionAndReturnString(const ::aidl::android::aidl::versioned::tests::BazUnion& in_u, std::string* _aidl_return) override {
+    return _impl->acceptUnionAndReturnString(in_u, _aidl_return);
+  }
+  ::ndk::ScopedAStatus ignoreParcelablesAndRepeatInt(const ::aidl::android::aidl::versioned::tests::Foo& in_inFoo, ::aidl::android::aidl::versioned::tests::Foo* in_inoutFoo, ::aidl::android::aidl::versioned::tests::Foo* out_outFoo, int32_t in_value, int32_t* _aidl_return) override {
+    return _impl->ignoreParcelablesAndRepeatInt(in_inFoo, in_inoutFoo, out_outFoo, in_value, _aidl_return);
+  }
+  ::ndk::ScopedAStatus returnsLengthOfFooArray(const std::vector<::aidl::android::aidl::versioned::tests::Foo>& in_foos, int32_t* _aidl_return) override {
+    return _impl->returnsLengthOfFooArray(in_foos, _aidl_return);
+  }
+protected:
+private:
+  std::shared_ptr<IFooInterface> _impl;
+};
+
 }  // namespace tests
 }  // namespace versioned
 }  // namespace aidl
diff --git a/tests/golden_output/aidl-test-versioned-interface-V1-rust-source/gen/android/aidl/versioned/tests/BazUnion.rs b/tests/golden_output/aidl-test-versioned-interface-V1-rust-source/gen/android/aidl/versioned/tests/BazUnion.rs
index 5e30930..b60ff88 100644
--- a/tests/golden_output/aidl-test-versioned-interface-V1-rust-source/gen/android/aidl/versioned/tests/BazUnion.rs
+++ b/tests/golden_output/aidl-test-versioned-interface-V1-rust-source/gen/android/aidl/versioned/tests/BazUnion.rs
@@ -37,6 +37,16 @@
 impl binder::binder_impl::ParcelableMetadata for BazUnion {
   fn get_descriptor() -> &'static str { "android.aidl.versioned.tests.BazUnion" }
 }
+pub mod Tag {
+  #![allow(non_upper_case_globals)]
+  use binder::declare_binder_enum;
+  declare_binder_enum! {
+    Tag : [i32; 1] {
+      intNum = 0,
+    }
+  }
+}
 pub(crate) mod mangled {
  pub use super::BazUnion as _7_android_4_aidl_9_versioned_5_tests_8_BazUnion;
+ pub use super::Tag::Tag as _7_android_4_aidl_9_versioned_5_tests_8_BazUnion_3_Tag;
 }
diff --git a/tests/golden_output/aidl_test_loggable_interface-cpp-source/gen/android/aidl/loggable/Union.cpp b/tests/golden_output/aidl_test_loggable_interface-cpp-source/gen/android/aidl/loggable/Union.cpp
index 092692e..09e1cc5 100644
--- a/tests/golden_output/aidl_test_loggable_interface-cpp-source/gen/android/aidl/loggable/Union.cpp
+++ b/tests/golden_output/aidl_test_loggable_interface-cpp-source/gen/android/aidl/loggable/Union.cpp
@@ -7,7 +7,7 @@
   ::android::status_t _aidl_ret_status;
   int32_t _aidl_tag;
   if ((_aidl_ret_status = _aidl_parcel->readInt32(&_aidl_tag)) != ::android::OK) return _aidl_ret_status;
-  switch (_aidl_tag) {
+  switch (static_cast<Tag>(_aidl_tag)) {
   case num: {
     int32_t _aidl_value;
     if ((_aidl_ret_status = _aidl_parcel->readInt32(&_aidl_value)) != ::android::OK) return _aidl_ret_status;
@@ -32,7 +32,7 @@
   return ::android::BAD_VALUE;
 }
 ::android::status_t Union::writeToParcel(::android::Parcel* _aidl_parcel) const {
-  ::android::status_t _aidl_ret_status = _aidl_parcel->writeInt32(getTag());
+  ::android::status_t _aidl_ret_status = _aidl_parcel->writeInt32(static_cast<int32_t>(getTag()));
   if (_aidl_ret_status != ::android::OK) return _aidl_ret_status;
   switch (getTag()) {
   case num: return _aidl_parcel->writeInt32(get<num>());
diff --git a/tests/golden_output/aidl_test_loggable_interface-cpp-source/gen/include/android/aidl/loggable/Union.h b/tests/golden_output/aidl_test_loggable_interface-cpp-source/gen/include/android/aidl/loggable/Union.h
index 918f117..9b67e56 100644
--- a/tests/golden_output/aidl_test_loggable_interface-cpp-source/gen/include/android/aidl/loggable/Union.h
+++ b/tests/golden_output/aidl_test_loggable_interface-cpp-source/gen/include/android/aidl/loggable/Union.h
@@ -1,6 +1,8 @@
 #pragma once
 
 #include <android/binder_to_string.h>
+#include <array>
+#include <binder/Enums.h>
 #include <binder/Parcel.h>
 #include <binder/Status.h>
 #include <cassert>
@@ -20,15 +22,18 @@
 namespace loggable {
 class Union : public ::android::Parcelable {
 public:
-  enum Tag : int32_t {
-    num = 0,  // int num;
-    str,  // String str;
+  enum class Tag : int32_t {
+    num = 0,
+    str = 1,
   };
+  // Expose tag symbols for legacy code
+  static const inline Tag num = Tag::num;
+  static const inline Tag str = Tag::str;
 
   template<typename _Tp>
   static constexpr bool _not_self = !std::is_same_v<std::remove_cv_t<std::remove_reference_t<_Tp>>, Union>;
 
-  Union() : _value(std::in_place_index<num>, int32_t(43)) { }
+  Union() : _value(std::in_place_index<static_cast<size_t>(num)>, int32_t(43)) { }
 
   template <typename _Tp, typename = std::enable_if_t<_not_self<_Tp>>>
   // NOLINTNEXTLINE(google-explicit-constructor)
@@ -41,12 +46,12 @@
 
   template <Tag _tag, typename... _Tp>
   static Union make(_Tp&&... _args) {
-    return Union(std::in_place_index<_tag>, std::forward<_Tp>(_args)...);
+    return Union(std::in_place_index<static_cast<size_t>(_tag)>, std::forward<_Tp>(_args)...);
   }
 
   template <Tag _tag, typename _Tp, typename... _Up>
   static Union make(std::initializer_list<_Tp> _il, _Up&&... _args) {
-    return Union(std::in_place_index<_tag>, std::move(_il), std::forward<_Up>(_args)...);
+    return Union(std::in_place_index<static_cast<size_t>(_tag)>, std::move(_il), std::forward<_Up>(_args)...);
   }
 
   Tag getTag() const {
@@ -56,18 +61,18 @@
   template <Tag _tag>
   const auto& get() const {
     if (getTag() != _tag) { __assert2(__FILE__, __LINE__, __PRETTY_FUNCTION__, "bad access: a wrong tag"); }
-    return std::get<_tag>(_value);
+    return std::get<static_cast<size_t>(_tag)>(_value);
   }
 
   template <Tag _tag>
   auto& get() {
     if (getTag() != _tag) { __assert2(__FILE__, __LINE__, __PRETTY_FUNCTION__, "bad access: a wrong tag"); }
-    return std::get<_tag>(_value);
+    return std::get<static_cast<size_t>(_tag)>(_value);
   }
 
   template <Tag _tag, typename... _Tp>
   void set(_Tp&&... _args) {
-    _value.emplace<_tag>(std::forward<_Tp>(_args)...);
+    _value.emplace<static_cast<size_t>(_tag)>(std::forward<_Tp>(_args)...);
   }
 
   inline bool operator!=(const Union& rhs) const {
@@ -111,3 +116,31 @@
 }  // namespace loggable
 }  // namespace aidl
 }  // namespace android
+namespace android {
+namespace aidl {
+namespace loggable {
+[[nodiscard]] static inline std::string toString(Union::Tag val) {
+  switch(val) {
+  case Union::Tag::num:
+    return "num";
+  case Union::Tag::str:
+    return "str";
+  default:
+    return std::to_string(static_cast<int32_t>(val));
+  }
+}
+}  // namespace loggable
+}  // namespace aidl
+}  // namespace android
+namespace android {
+namespace internal {
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wc++17-extensions"
+template <>
+constexpr inline std::array<::android::aidl::loggable::Union::Tag, 2> enum_values<::android::aidl::loggable::Union::Tag> = {
+  ::android::aidl::loggable::Union::Tag::num,
+  ::android::aidl::loggable::Union::Tag::str,
+};
+#pragma clang diagnostic pop
+}  // namespace internal
+}  // namespace android
diff --git a/tests/golden_output/aidl_test_loggable_interface-java-source/gen/android/aidl/loggable/Data.java b/tests/golden_output/aidl_test_loggable_interface-java-source/gen/android/aidl/loggable/Data.java
index 5e54a99..b0072c7 100644
--- a/tests/golden_output/aidl_test_loggable_interface-java-source/gen/android/aidl/loggable/Data.java
+++ b/tests/golden_output/aidl_test_loggable_interface-java-source/gen/android/aidl/loggable/Data.java
@@ -26,7 +26,7 @@
     _aidl_parcel.writeInt(0);
     _aidl_parcel.writeInt(num);
     _aidl_parcel.writeString(str);
-    _aidl_parcel.writeTypedObject(nestedUnion, 0);
+    _aidl_parcel.writeTypedObject(nestedUnion, _aidl_flag);
     _aidl_parcel.writeByte(nestedEnum);
     int _aidl_end_pos = _aidl_parcel.dataPosition();
     _aidl_parcel.setDataPosition(_aidl_start_pos);
diff --git a/tests/golden_output/aidl_test_loggable_interface-java-source/gen/android/aidl/loggable/Union.java b/tests/golden_output/aidl_test_loggable_interface-java-source/gen/android/aidl/loggable/Union.java
index 1751db3..f85770f 100644
--- a/tests/golden_output/aidl_test_loggable_interface-java-source/gen/android/aidl/loggable/Union.java
+++ b/tests/golden_output/aidl_test_loggable_interface-java-source/gen/android/aidl/loggable/Union.java
@@ -127,4 +127,8 @@
     this._tag = _tag;
     this._value = _value;
   }
+  public static @interface Tag {
+    public static final int num = 0;
+    public static final int str = 1;
+  }
 }
diff --git a/tests/golden_output/aidl_test_loggable_interface-ndk-source/gen/android/aidl/loggable/Union.cpp b/tests/golden_output/aidl_test_loggable_interface-ndk-source/gen/android/aidl/loggable/Union.cpp
index aec5ed6..2d8191e 100644
--- a/tests/golden_output/aidl_test_loggable_interface-ndk-source/gen/android/aidl/loggable/Union.cpp
+++ b/tests/golden_output/aidl_test_loggable_interface-ndk-source/gen/android/aidl/loggable/Union.cpp
@@ -12,7 +12,7 @@
   binder_status_t _aidl_ret_status;
   int32_t _aidl_tag;
   if ((_aidl_ret_status = ::ndk::AParcel_readData(_parcel, &_aidl_tag)) != STATUS_OK) return _aidl_ret_status;
-  switch (_aidl_tag) {
+  switch (static_cast<Tag>(_aidl_tag)) {
   case num: {
     int32_t _aidl_value;
     if ((_aidl_ret_status = ::ndk::AParcel_readData(_parcel, &_aidl_value)) != STATUS_OK) return _aidl_ret_status;
@@ -37,7 +37,7 @@
   return STATUS_BAD_VALUE;
 }
 binder_status_t Union::writeToParcel(AParcel* _parcel) const {
-  binder_status_t _aidl_ret_status = ::ndk::AParcel_writeData(_parcel, getTag());
+  binder_status_t _aidl_ret_status = ::ndk::AParcel_writeData(_parcel, static_cast<int32_t>(getTag()));
   if (_aidl_ret_status != STATUS_OK) return _aidl_ret_status;
   switch (getTag()) {
   case num: return ::ndk::AParcel_writeData(_parcel, get<num>());
diff --git a/tests/golden_output/aidl_test_loggable_interface-ndk-source/gen/include/aidl/android/aidl/loggable/BnLoggableInterface.h b/tests/golden_output/aidl_test_loggable_interface-ndk-source/gen/include/aidl/android/aidl/loggable/BnLoggableInterface.h
index 650ca86..d6f61e1 100644
--- a/tests/golden_output/aidl_test_loggable_interface-ndk-source/gen/include/aidl/android/aidl/loggable/BnLoggableInterface.h
+++ b/tests/golden_output/aidl_test_loggable_interface-ndk-source/gen/include/aidl/android/aidl/loggable/BnLoggableInterface.h
@@ -3,6 +3,13 @@
 #include "aidl/android/aidl/loggable/ILoggableInterface.h"
 
 #include <android/binder_ibinder.h>
+#include <cassert>
+
+#ifndef __BIONIC__
+#ifndef __assert2
+#define __assert2(a,b,c,d) ((void)0)
+#endif
+#endif
 
 namespace aidl {
 namespace android {
@@ -31,6 +38,19 @@
   ::ndk::SpAIBinder createBinder() override;
 private:
 };
+class ILoggableInterfaceDelegator : public BnLoggableInterface {
+public:
+  explicit ILoggableInterfaceDelegator(const std::shared_ptr<ILoggableInterface> &impl) : _impl(impl) {
+  }
+
+  ::ndk::ScopedAStatus LogThis(bool in_boolValue, std::vector<bool>* in_boolArray, int8_t in_byteValue, std::vector<uint8_t>* in_byteArray, char16_t in_charValue, std::vector<char16_t>* in_charArray, int32_t in_intValue, std::vector<int32_t>* in_intArray, int64_t in_longValue, std::vector<int64_t>* in_longArray, float in_floatValue, std::vector<float>* in_floatArray, double in_doubleValue, std::vector<double>* in_doubleArray, const std::string& in_stringValue, std::vector<std::string>* in_stringArray, std::vector<std::string>* in_listValue, const ::aidl::android::aidl::loggable::Data& in_dataValue, const ::ndk::SpAIBinder& in_binderValue, ::ndk::ScopedFileDescriptor* in_pfdValue, std::vector<::ndk::ScopedFileDescriptor>* in_pfdArray, std::vector<std::string>* _aidl_return) override {
+    return _impl->LogThis(in_boolValue, in_boolArray, in_byteValue, in_byteArray, in_charValue, in_charArray, in_intValue, in_intArray, in_longValue, in_longArray, in_floatValue, in_floatArray, in_doubleValue, in_doubleArray, in_stringValue, in_stringArray, in_listValue, in_dataValue, in_binderValue, in_pfdValue, in_pfdArray, _aidl_return);
+  }
+protected:
+private:
+  std::shared_ptr<ILoggableInterface> _impl;
+};
+
 }  // namespace loggable
 }  // namespace aidl
 }  // namespace android
diff --git a/tests/golden_output/aidl_test_loggable_interface-ndk-source/gen/include/aidl/android/aidl/loggable/Union.h b/tests/golden_output/aidl_test_loggable_interface-ndk-source/gen/include/aidl/android/aidl/loggable/Union.h
index ceb63ac..ef0532f 100644
--- a/tests/golden_output/aidl_test_loggable_interface-ndk-source/gen/include/aidl/android/aidl/loggable/Union.h
+++ b/tests/golden_output/aidl_test_loggable_interface-ndk-source/gen/include/aidl/android/aidl/loggable/Union.h
@@ -1,5 +1,6 @@
 #pragma once
 
+#include <array>
 #include <cassert>
 #include <cstdint>
 #include <memory>
@@ -9,6 +10,7 @@
 #include <utility>
 #include <variant>
 #include <vector>
+#include <android/binder_enums.h>
 #include <android/binder_interface_utils.h>
 #include <android/binder_parcelable_utils.h>
 #include <android/binder_to_string.h>
@@ -29,15 +31,19 @@
   typedef std::false_type fixed_size;
   static const char* descriptor;
 
-  enum Tag : int32_t {
-    num = 0,  // int num;
-    str,  // String str;
+  enum class Tag : int32_t {
+    num = 0,
+    str = 1,
   };
 
+  // Expose tag symbols for legacy code
+  static const inline Tag num = Tag::num;
+  static const inline Tag str = Tag::str;
+
   template<typename _Tp>
   static constexpr bool _not_self = !std::is_same_v<std::remove_cv_t<std::remove_reference_t<_Tp>>, Union>;
 
-  Union() : _value(std::in_place_index<num>, int32_t(43)) { }
+  Union() : _value(std::in_place_index<static_cast<size_t>(num)>, int32_t(43)) { }
 
   template <typename _Tp, typename = std::enable_if_t<_not_self<_Tp>>>
   // NOLINTNEXTLINE(google-explicit-constructor)
@@ -50,12 +56,12 @@
 
   template <Tag _tag, typename... _Tp>
   static Union make(_Tp&&... _args) {
-    return Union(std::in_place_index<_tag>, std::forward<_Tp>(_args)...);
+    return Union(std::in_place_index<static_cast<size_t>(_tag)>, std::forward<_Tp>(_args)...);
   }
 
   template <Tag _tag, typename _Tp, typename... _Up>
   static Union make(std::initializer_list<_Tp> _il, _Up&&... _args) {
-    return Union(std::in_place_index<_tag>, std::move(_il), std::forward<_Up>(_args)...);
+    return Union(std::in_place_index<static_cast<size_t>(_tag)>, std::move(_il), std::forward<_Up>(_args)...);
   }
 
   Tag getTag() const {
@@ -65,18 +71,18 @@
   template <Tag _tag>
   const auto& get() const {
     if (getTag() != _tag) { __assert2(__FILE__, __LINE__, __PRETTY_FUNCTION__, "bad access: a wrong tag"); }
-    return std::get<_tag>(_value);
+    return std::get<static_cast<size_t>(_tag)>(_value);
   }
 
   template <Tag _tag>
   auto& get() {
     if (getTag() != _tag) { __assert2(__FILE__, __LINE__, __PRETTY_FUNCTION__, "bad access: a wrong tag"); }
-    return std::get<_tag>(_value);
+    return std::get<static_cast<size_t>(_tag)>(_value);
   }
 
   template <Tag _tag, typename... _Tp>
   void set(_Tp&&... _args) {
-    _value.emplace<_tag>(std::forward<_Tp>(_args)...);
+    _value.emplace<static_cast<size_t>(_tag)>(std::forward<_Tp>(_args)...);
   }
 
   binder_status_t readFromParcel(const AParcel* _parcel);
@@ -119,3 +125,33 @@
 }  // namespace aidl
 }  // namespace android
 }  // namespace aidl
+namespace aidl {
+namespace android {
+namespace aidl {
+namespace loggable {
+[[nodiscard]] static inline std::string toString(Union::Tag val) {
+  switch(val) {
+  case Union::Tag::num:
+    return "num";
+  case Union::Tag::str:
+    return "str";
+  default:
+    return std::to_string(static_cast<int32_t>(val));
+  }
+}
+}  // namespace loggable
+}  // namespace aidl
+}  // namespace android
+}  // namespace aidl
+namespace ndk {
+namespace internal {
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wc++17-extensions"
+template <>
+constexpr inline std::array<aidl::android::aidl::loggable::Union::Tag, 2> enum_values<aidl::android::aidl::loggable::Union::Tag> = {
+  aidl::android::aidl::loggable::Union::Tag::num,
+  aidl::android::aidl::loggable::Union::Tag::str,
+};
+#pragma clang diagnostic pop
+}  // namespace internal
+}  // namespace ndk
diff --git a/tests/golden_test.sh b/tests/golden_test.sh
index 9d95b90..86a5d1a 100755
--- a/tests/golden_test.sh
+++ b/tests/golden_test.sh
@@ -48,7 +48,7 @@
     "aidl_test_loggable_interface-cpp-source"
     "aidl_test_loggable_interface-java-source"
     "aidl_test_loggable_interface-ndk-source"
-    "aidl-test-interface-platform-java-source"
+    "aidl-test-interface-permission-java-source"
     "aidl-test-fixedsizearray-cpp-source"
     "aidl-test-fixedsizearray-java-source"
     "aidl-test-fixedsizearray-ndk-source"
diff --git a/tests/java/src/android/aidl/service/TestServiceServer.java b/tests/java/src/android/aidl/service/TestServiceServer.java
index ad98416..51a7520 100644
--- a/tests/java/src/android/aidl/service/TestServiceServer.java
+++ b/tests/java/src/android/aidl/service/TestServiceServer.java
@@ -592,6 +592,15 @@
     return new MyNewName();
   }
 
+  @Override
+  public int[] GetUnionTags(Union[] input) throws RemoteException {
+    int[] tags = new int[input.length];
+    for (int i = 0; i < input.length; i++) {
+      tags[i] = input[i].getTag();
+    }
+    return tags;
+  }
+
   class MyCppJavaTests extends ICppJavaTests.Stub {
     @Override
     public BadParcelable RepeatBadParcelable(BadParcelable input) throws RemoteException {
diff --git a/tests/java/src/android/aidl/tests/TestServiceClient.java b/tests/java/src/android/aidl/tests/TestServiceClient.java
index e9140cc..098ad20 100644
--- a/tests/java/src/android/aidl/tests/TestServiceClient.java
+++ b/tests/java/src/android/aidl/tests/TestServiceClient.java
@@ -757,10 +757,10 @@
             + "f: 17, "
             + "shouldBeJerry: Jerry, "
             + "shouldBeByteBar: 2, "
-            + "shouldBeIntBar: 2000, "
+            + "shouldBeIntBar: BAR, "
             + "shouldBeLongBar: 200000000000, "
             + "shouldContainTwoByteFoos: [1, 1], "
-            + "shouldContainTwoIntFoos: [1000, 1000], "
+            + "shouldContainTwoIntFoos: [FOO, FOO], "
             + "shouldContainTwoLongFoos: [100000000000, 100000000000], "
             + "stringDefaultsToFoo: foo, "
             + "byteDefaultsToFour: 4, "
@@ -809,7 +809,7 @@
             + "shouldSetBit0AndBit2: 5, "
             + "u: android.aidl.tests.Union.ns([1, 2, 3]), "
             + "shouldBeConstS1: android.aidl.tests.Union.s(a string constant in union), "
-            + "defaultWithFoo: 1000"
+            + "defaultWithFoo: FOO"
             + "}";
         assertThat(p.toString(), is(expected));
     }
@@ -888,8 +888,8 @@
             + "parcelableArray: ["
             + "android.aidl.tests.OtherParcelableForToString{field: other}, "
             + "android.aidl.tests.OtherParcelableForToString{field: other}], "
-            + "enumValue: 1000, "
-            + "enumArray: [1000, 2000], "
+            + "enumValue: FOO, "
+            + "enumArray: [FOO, BAR], "
             + "nullArray: null, "
             + "nullList: null, "
             + "parcelableGeneric: android.aidl.tests.GenericStructuredParcelable{a: 1, b: 2}, "
@@ -900,6 +900,21 @@
     }
 
     @Test
+    public void testEnumToString() {
+      assertThat(IntEnum.$.toString(IntEnum.FOO), is("FOO"));
+      assertThat(IntEnum.$.toString(0), is("0"));
+      assertThat(IntEnum.$.arrayToString(null), is("null"));
+      assertThat(IntEnum.$.arrayToString(new int[] {}), is("[]"));
+      assertThat(IntEnum.$.arrayToString(new int[] {IntEnum.FOO, IntEnum.BAR}), is("[FOO, BAR]"));
+      assertThat(IntEnum.$.arrayToString(new int[] {IntEnum.FOO, 0}), is("[FOO, 0]"));
+      assertThat(IntEnum.$.arrayToString(new int[][] {{IntEnum.FOO, IntEnum.BAR}, {IntEnum.BAZ}}),
+          is("[[FOO, BAR], [BAZ]]"));
+      assertThrows(IllegalArgumentException.class, () -> IntEnum.$.arrayToString(IntEnum.FOO));
+      assertThrows(
+          IllegalArgumentException.class, () -> IntEnum.$.arrayToString(new long[] {LongEnum.FOO}));
+    }
+
+    @Test
     public void testRenamedInterface() throws RemoteException {
       IOldName oldAsOld = service.GetOldNameInterface();
       assertNotNull(oldAsOld);
@@ -956,6 +971,13 @@
     }
 
     @Test
+    public void testGetUnionTags() throws RemoteException {
+      assertArrayEquals(new int[] {}, service.GetUnionTags(new Union[] {}));
+      assertArrayEquals(new int[] {Union.n, Union.ns},
+          service.GetUnionTags(new Union[] {Union.n(0), Union.ns(new int[] {})}));
+    }
+
+    @Test
     public void testDescribeContents() throws Exception {
       CompilerChecks cc = new CompilerChecks();
       cc.pfd_array = new ParcelFileDescriptor[] {null, null, null};
diff --git a/tests/rust/test_client.rs b/tests/rust/test_client.rs
index 1d75234..81315c9 100644
--- a/tests/rust/test_client.rs
+++ b/tests/rust/test_client.rs
@@ -822,6 +822,15 @@
 }
 
 #[test]
+fn test_get_union_tags() {
+    let service = get_test_service();
+    let result = service.GetUnionTags(&[]);
+    assert_eq!(result, Ok(vec![]));
+    let result = service.GetUnionTags(&[Union::Union::N(0), Union::Union::Ns(vec![])]);
+    assert_eq!(result, Ok(vec![Union::Tag::Tag::n, Union::Tag::Tag::ns]));
+}
+
+#[test]
 fn test_unions() {
     assert_eq!(Union::Union::default(), Union::Union::Ns(vec![]));
     assert_eq!(EnumUnion::default(), EnumUnion::IntEnum(IntEnum::FOO));
@@ -887,7 +896,7 @@
             .expect("did not get binder service");
 
     let ret = service.acceptUnionAndReturnString(&BazUnion::LongNum(42));
-    assert!(!ret.is_ok());
+    assert!(ret.is_err());
 
     let main_service = get_test_service();
     let backend = main_service.getBackendType().expect("error getting backend type");
diff --git a/tests/rust/test_service.rs b/tests/rust/test_service.rs
index 1d3c092..3a5c09c 100644
--- a/tests/rust/test_service.rs
+++ b/tests/rust/test_service.rs
@@ -441,6 +441,21 @@
         Ok(INewName::BnNewName::new_binder(NewName, BinderFeatures::default()))
     }
 
+    fn GetUnionTags(&self, input: &[Union::Union]) -> binder::Result<Vec<Union::Tag::Tag>> {
+        Ok(input
+            .iter()
+            .map(|u| match u {
+                Union::Union::Ns(_) => Union::Tag::Tag::ns,
+                Union::Union::N(_) => Union::Tag::Tag::n,
+                Union::Union::M(_) => Union::Tag::Tag::m,
+                Union::Union::S(_) => Union::Tag::Tag::s,
+                Union::Union::Ibinder(_) => Union::Tag::Tag::ibinder,
+                Union::Union::Ss(_) => Union::Tag::Tag::ss,
+                Union::Union::Be(_) => Union::Tag::Tag::be,
+            })
+            .collect::<Vec<_>>())
+    }
+
     fn GetCppJavaTests(&self) -> binder::Result<Option<SpIBinder>> {
         Ok(None)
     }
diff --git a/tests/rust/test_service_async.rs b/tests/rust/test_service_async.rs
index f6cd039..cfc0f28 100644
--- a/tests/rust/test_service_async.rs
+++ b/tests/rust/test_service_async.rs
@@ -483,6 +483,21 @@
         Ok(INewName::BnNewName::new_async_binder(NewName, rt(), BinderFeatures::default()))
     }
 
+    async fn GetUnionTags(&self, input: &[Union::Union]) -> binder::Result<Vec<Union::Tag::Tag>> {
+        Ok(input
+            .iter()
+            .map(|u| match u {
+                Union::Union::Ns(_) => Union::Tag::Tag::ns,
+                Union::Union::N(_) => Union::Tag::Tag::n,
+                Union::Union::M(_) => Union::Tag::Tag::m,
+                Union::Union::S(_) => Union::Tag::Tag::s,
+                Union::Union::Ibinder(_) => Union::Tag::Tag::ibinder,
+                Union::Union::Ss(_) => Union::Tag::Tag::ss,
+                Union::Union::Be(_) => Union::Tag::Tag::be,
+            })
+            .collect::<Vec<_>>())
+    }
+
     async fn GetCppJavaTests(&self) -> binder::Result<Option<SpIBinder>> {
         Ok(None)
     }