Check the availability of types against SDK version

List<interface>, interface[]: at least Tiramisu in Java
ParcelableHolder: at least 31

Build system uses "current" as default min_sdk_version for
aidl_interface modules.

Bug: 205070744
Test: aidl_unittests
Test: aidl_integration_test
Change-Id: I8f7e86c4f87a8fd64b6f5b995000b3e32d7328b0
diff --git a/Android.bp b/Android.bp
index 0994f44..2cc0aa1 100644
--- a/Android.bp
+++ b/Android.bp
@@ -89,6 +89,7 @@
         "aidl_typenames.cpp",
         "aidl.cpp",
         "ast_java.cpp",
+        "check_valid.cpp",
         "code_writer.cpp",
         "comments.cpp",
         "diagnostics.cpp",
diff --git a/aidl.cpp b/aidl.cpp
index 90b593a..568ccb8 100644
--- a/aidl.cpp
+++ b/aidl.cpp
@@ -40,6 +40,7 @@
 #include "aidl_dumpapi.h"
 #include "aidl_language.h"
 #include "aidl_typenames.h"
+#include "check_valid.h"
 #include "generate_aidl_mappings.h"
 #include "generate_cpp.h"
 #include "generate_java.h"
@@ -596,6 +597,10 @@
     VisitTopDown([](const AidlNode& n) { n.MarkVisited(); }, *doc);
   }
 
+  if (!CheckValid(*document, options)) {
+    return AidlError::BAD_TYPE;
+  }
+
   if (!ValidateAnnotationContext(*document)) {
     return AidlError::BAD_TYPE;
   }
diff --git a/aidl_unittest.cpp b/aidl_unittest.cpp
index d4c5321..fd912c0 100644
--- a/aidl_unittest.cpp
+++ b/aidl_unittest.cpp
@@ -95,6 +95,7 @@
     io_delegate_.SetFileContents(path, contents);
     vector<string> args;
     args.emplace_back("aidl");
+    args.emplace_back("--min_sdk_version=current");
     args.emplace_back("--lang=" + to_string(lang));
     for (const string& s : additional_arguments) {
       args.emplace_back(s);
@@ -2087,6 +2088,7 @@
   EXPECT_NE(nullptr, Parse("a/Data.aidl", extendable_parcelable, typenames_, GetLanguage()));
   EXPECT_EQ("", GetCapturedStderr());
 }
+
 TEST_P(AidlTest, ParcelableHolderAsReturnType) {
   CaptureStderr();
   string parcelableholder_return_interface =
@@ -4855,6 +4857,26 @@
   EXPECT_THAT(GetCapturedStderr(), HasSubstr("'void' is an invalid type for the parameter 'n'"));
 }
 
+TEST_F(AidlTest, InterfaceVectorIsAvailableAfterTiramisu) {
+  io_delegate_.SetFileContents("p/IFoo.aidl",
+                               "interface IFoo{\n"
+                               "  void foo(in IFoo[] n);\n"
+                               "  void bar(in List<IFoo> n);\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("Array of interfaces is available since"));
+  EXPECT_THAT(captured_stderr, HasSubstr("List of interfaces is available since"));
+
+  CaptureStderr();
+  EXPECT_TRUE(
+      compile_aidl(Options::From("aidl --lang=java --min_sdk_version Tiramisu -o out p/IFoo.aidl"),
+                   io_delegate_));
+  EXPECT_EQ(GetCapturedStderr(), "");
+}
+
 struct TypeParam {
   string kind;
   string literal;
@@ -5023,8 +5045,8 @@
     }
     io.SetFileContents("a/Target.aidl", "package a; parcelable Target { " + decl + " f; }");
 
-    const auto options =
-        Options::From(fmt::format("aidl -I . --lang={} a/Target.aidl -o out -h out", lang));
+    const auto options = Options::From(fmt::format(
+        "aidl -I . --min_sdk_version current --lang={} a/Target.aidl -o out -h out", lang));
     CaptureStderr();
     compile_aidl(options, io);
     auto it = expectations.find(lang + "_" + kind);
diff --git a/build/aidl_gen_rule.go b/build/aidl_gen_rule.go
index f843e09..6d71d9b 100644
--- a/build/aidl_gen_rule.go
+++ b/build/aidl_gen_rule.go
@@ -192,10 +192,8 @@
 	if g.properties.Platform_apis {
 		optionalFlags = append(optionalFlags, "--min_sdk_version platform_apis")
 	} else {
-		minSdkVer := g.properties.Min_sdk_version
-		if minSdkVer != nil {
-			optionalFlags = append(optionalFlags, "--min_sdk_version "+*minSdkVer)
-		}
+		minSdkVer := proptools.StringDefault(g.properties.Min_sdk_version, "current")
+		optionalFlags = append(optionalFlags, "--min_sdk_version "+minSdkVer)
 	}
 	optionalFlags = append(optionalFlags, wrap("-p", g.deps.preprocessed.Strings(), "")...)
 
diff --git a/build/aidl_interface_backends.go b/build/aidl_interface_backends.go
index b6f47ea..6917814 100644
--- a/build/aidl_interface_backends.go
+++ b/build/aidl_interface_backends.go
@@ -212,12 +212,16 @@
 		// Don't create a library for the yet-to-be-frozen version.
 		return ""
 	}
-
+	minSdkVersion := i.minSdkVersion(langJava)
 	sdkVersion := i.properties.Backend.Java.Sdk_version
 	if !proptools.Bool(i.properties.Backend.Java.Platform_apis) && sdkVersion == nil {
 		// platform apis requires no default
 		sdkVersion = proptools.StringPtr("system_current")
 	}
+	// use sdkVersion if minSdkVersion is not set
+	if sdkVersion != nil && minSdkVersion == nil {
+		minSdkVersion = proptools.StringPtr(android.SdkSpecFrom(mctx, *sdkVersion).ApiLevel.String())
+	}
 
 	mctx.CreateModule(aidlGenFactory, &nameProperties{
 		Name: proptools.StringPtr(javaSourceGen),
@@ -226,7 +230,7 @@
 		AidlRoot:              aidlRoot,
 		ImportsWithoutVersion: i.properties.ImportsWithoutVersion,
 		Stability:             i.properties.Stability,
-		Min_sdk_version:       i.minSdkVersion(langJava),
+		Min_sdk_version:       minSdkVersion,
 		Platform_apis:         proptools.Bool(i.properties.Backend.Java.Platform_apis),
 		Lang:                  langJava,
 		BaseName:              i.ModuleBase.Name(),
diff --git a/build/aidl_test.go b/build/aidl_test.go
index 9db4b3e..280c7c8 100644
--- a/build/aidl_test.go
+++ b/build/aidl_test.go
@@ -1379,6 +1379,22 @@
 	}
 }
 
+func TestAidlModuleJavaSdkVersionDeterminesMinSdkVersion(t *testing.T) {
+	ctx, _ := testAidl(t, `
+		aidl_interface {
+			name: "myiface",
+			srcs: ["a/Foo.aidl"],
+			backend: {
+				java: {
+					sdk_version: "28",
+				},
+			},
+		}
+	`, java.FixtureWithPrebuiltApis(map[string][]string{"28": {"foo"}}))
+	params := ctx.ModuleForTests("myiface-V1-java-source", "").Output("a/Foo.java")
+	assertContains(t, params.Args["optionalFlags"], "--min_sdk_version 28")
+}
+
 func TestAidlModuleNameContainsVersion(t *testing.T) {
 	testAidlError(t, "aidl_interface should not have '-V<number> suffix", `
 		aidl_interface {
diff --git a/check_valid.cpp b/check_valid.cpp
new file mode 100644
index 0000000..e7bcb8d
--- /dev/null
+++ b/check_valid.cpp
@@ -0,0 +1,88 @@
+/*
+ * Copyright (C) 2021, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "check_valid.h"
+#include "aidl.h"
+
+#include <vector>
+
+namespace android {
+namespace aidl {
+
+using TypePredicate = std::function<bool(const AidlTypeSpecifier&)>;
+
+namespace {
+bool IsListOf(const AidlTypeSpecifier& type, TypePredicate pred) {
+  return type.GetName() == "List" && type.IsGeneric() && type.GetTypeParameters().size() == 1 &&
+         pred(*type.GetTypeParameters().at(0));
+}
+bool IsArrayOf(const AidlTypeSpecifier& type, TypePredicate pred) {
+  return type.IsArray() && pred(type);
+}
+bool IsInterface(const AidlTypeSpecifier& type) {
+  return type.GetDefinedType() && type.GetDefinedType()->AsInterface();
+}
+}  // namespace
+
+struct CheckTypeVisitor : AidlVisitor {
+  bool success = true;
+  std::vector<TypePredicate> checkers;
+
+  void Visit(const AidlTypeSpecifier& type) override {
+    for (auto& checker : checkers) {
+      if (!checker(type)) {
+        success = false;
+      }
+    }
+  }
+
+  void Check(TypePredicate checker) { checkers.push_back(std::move(checker)); }
+};
+
+bool CheckValid(const AidlDocument& doc, const Options& options) {
+  const auto lang = options.TargetLanguage();
+  const auto min_sdk_version = options.GetMinSdkVersion();
+
+  CheckTypeVisitor v;
+
+  v.Check([&](const AidlTypeSpecifier& type) {
+    const auto valid_version = MinSdkVersionFromString("Tiramisu").value();
+    if ((IsListOf(type, IsInterface) || IsArrayOf(type, IsInterface)) &&
+        lang == Options::Language::JAVA && min_sdk_version < valid_version) {
+      const auto kind = IsListOf(type, IsInterface) ? "List" : "Array";
+      AIDL_ERROR(type) << kind << " of interfaces is available since SDK = " << valid_version
+                       << " in Java. Current min_sdk_version is " << min_sdk_version << ".";
+      return false;
+    }
+    return true;
+  });
+
+  v.Check([&](const AidlTypeSpecifier& type) {
+    const auto valid_version = MinSdkVersionFromString("S").value();
+    if (type.GetName() == "ParcelableHolder" && min_sdk_version < valid_version) {
+      AIDL_ERROR(type) << " ParcelableHolder is available since SDK = " << valid_version
+                       << ". Current min_sdk_version is " << min_sdk_version << ".";
+      return false;
+    }
+    return true;
+  });
+
+  VisitTopDown(v, doc);
+  return v.success;
+}
+
+}  // namespace aidl
+}  // namespace android
diff --git a/check_valid.h b/check_valid.h
new file mode 100644
index 0000000..0fdd220
--- /dev/null
+++ b/check_valid.h
@@ -0,0 +1,28 @@
+/*
+ * Copyright (C) 2021, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include "aidl_language.h"
+#include "options.h"
+
+namespace android {
+namespace aidl {
+
+bool CheckValid(const AidlDocument& doc, const Options& options);
+
+}
+}  // namespace android
\ No newline at end of file
diff --git a/options.cpp b/options.cpp
index 13fad03..3148a64 100644
--- a/options.cpp
+++ b/options.cpp
@@ -202,7 +202,7 @@
     {"platform_apis", 10001},
 };
 
-static Result<uint32_t> MinSdkVersionFromString(const std::string& str) {
+Result<uint32_t> MinSdkVersionFromString(const std::string& str) {
   uint32_t num;
   if (!android::base::ParseUint(str, &num, 10000u /* max */)) {
     if (auto found = codeNameToVersion.find(str); found != codeNameToVersion.end()) {
@@ -586,6 +586,14 @@
       return;
     }
   }
+  if (task_ != Options::Task::COMPILE) {
+    if (min_sdk_version_ != 0) {
+      error_message_ << "--min_sdk_version is available only for compilation." << endl;
+      return;
+    }
+    // For other tasks, use "current"
+    min_sdk_version_ = MinSdkVersionFromString("current").value();
+  }
 
   uint32_t default_ver = DefaultMinSdkVersionForLang(language_);
   if (min_sdk_version_ == 0) {  // --min_sdk_version flag not specified
diff --git a/options.h b/options.h
index a391332..2394395 100644
--- a/options.h
+++ b/options.h
@@ -19,6 +19,8 @@
 #include <string>
 #include <vector>
 
+#include <android-base/result.h>
+
 #include "diagnostics.h"
 
 namespace android {
@@ -211,6 +213,7 @@
 };
 
 std::string to_string(Options::Language language);
+android::base::Result<uint32_t> MinSdkVersionFromString(const std::string& str);
 
 }  // namespace aidl
 }  // namespace android