Check AIDL vintf interface extensions for frozen hash

If a frozen vintf interface has an extension that also has vintf
stability, it must also be frozen.

Test: unfreeze av-audio-types-aidl, mark it vintf, attach it
Test: atest vts_vintf_treble_framework_test

Bug: 338410350
Change-Id: Ie314859d2625f943447531b919a5aaabe9fec369
diff --git a/treble/vintf/SingleManifestTest.cpp b/treble/vintf/SingleManifestTest.cpp
index a021aff..948f13e 100644
--- a/treble/vintf/SingleManifestTest.cpp
+++ b/treble/vintf/SingleManifestTest.cpp
@@ -24,6 +24,7 @@
 #include <android/apex/IApexService.h>
 #include <binder/IServiceManager.h>
 #include <binder/Parcel.h>
+#include <binder/Stability.h>
 #include <binder/Status.h>
 #include <dirent.h>
 #include <dlfcn.h>
@@ -45,6 +46,11 @@
 namespace vintf {
 namespace testing {
 
+namespace {
+
+constexpr int kAndroidApi202404 = 202404;
+
+}  // namespace
 using android::FqInstance;
 using android::vintf::toFQNameString;
 
@@ -641,6 +647,41 @@
   return false;
 }
 
+// This checks to make sure all vintf extensions are frozen.
+// We do not check for known hashes because the Android framework does not
+// support these extensions without out-of-tree changes from partners.
+// @param binder - the parent binder to check all of its extensions
+void checkVintfExtensionInterfaces(const sp<IBinder> &binder, bool is_release) {
+  // if you end up here because of a stack overflow when running this
+  // test... you have a cycle in your interface extensions. Break that
+  // cycle to continue.
+  if (!binder) return;
+  sp<IBinder> extension;
+  status_t status = binder->getExtension(&extension);
+  if (status != OK || !extension) return;
+
+  if (android::internal::Stability::requiresVintfDeclaration(extension)) {
+    const std::string hash = getInterfaceHash(extension);
+    if (hash.empty() || hash == "notfrozen") {
+      if (is_release) {
+        ADD_FAILURE() << "Interface extension "
+                      << extension->getInterfaceDescriptor()
+                      << " is unfrozen! It is attached to "
+                      << " a binder for frozen VINTF interface ("
+                      << binder->getInterfaceDescriptor()
+                      << " so it must also be frozen.";
+      } else {
+        std::cout << "INFO: missing hash for vintf interface extension "
+                  << binder->getInterfaceDescriptor()
+                  << " which is attached to "
+                  << binder->getInterfaceDescriptor()
+                  << ". This will become an error upon release." << std::endl;
+      }
+    }
+  }
+  checkVintfExtensionInterfaces(extension, is_release);
+}
+
 // An AIDL HAL with VINTF stability can only be registered if it is in the
 // manifest. However, we still must manually check that every declared HAL is
 // actually present on the device.
@@ -701,7 +742,7 @@
       }
     }
   } else {
-    // is extension
+    // is partner-owned
     //
     // we only require that these are frozen, but we cannot check them for
     // accuracy
@@ -715,6 +756,9 @@
       }
     }
   }
+  if (GetBoardApiLevel() >= kAndroidApi202404) {
+    checkVintfExtensionInterfaces(binder, is_release);
+  }
 }
 
 // We don't want to add more same process HALs in Android. We have some 3rd