Support @Immutable for a parcelable

In Java, an immutable parcelable has only final field.
And with regard to List or Map, it is always unmodifiableList or Map

And also, it can be return type or in argument in an interface.

For the other backend, it doesn't affect anything.

Bug: 161506914
Test: atest aidl_integration_test aidl_unittests
Change-Id: If0aee76ac26554c087031371c88f827e56be1d98
diff --git a/Android.bp b/Android.bp
index 06f8636..580e5a1 100644
--- a/Android.bp
+++ b/Android.bp
@@ -332,11 +332,13 @@
     srcs: [
         "tests/android/aidl/tests/*.aidl",
         "tests/android/aidl/tests/generic/*.aidl",
+        "tests/android/aidl/tests/immutable/*.aidl",
         "tests/android/aidl/tests/map/*.aidl",
         "tests/android/aidl/tests/extension/*.aidl",
         "tests/java_app/src/android/aidl/tests/ExtensionTests.java",
         "tests/java_app/src/android/aidl/tests/GenericTests.java",
         "tests/java_app/src/android/aidl/tests/MapTests.java",
+        "tests/java_app/src/android/aidl/tests/ImmutableAnnotationTests.java",
         "tests/java_app/src/android/aidl/tests/NullableTests.java",
         "tests/java_app/src/android/aidl/tests/SimpleParcelable.java",
         "tests/java_app/src/android/aidl/tests/TestFailException.java",
diff --git a/aidl_checkapi.cpp b/aidl_checkapi.cpp
index 5a4daa6..9e73f31 100644
--- a/aidl_checkapi.cpp
+++ b/aidl_checkapi.cpp
@@ -52,6 +52,7 @@
   static const set<AidlAnnotation::Type> kIgnoreAnnotations{
       AidlAnnotation::Type::NULLABLE,
       AidlAnnotation::Type::JAVA_DEBUG,
+      AidlAnnotation::Type::IMMUTABLE,
   };
   set<AidlAnnotation> annotations;
   for (const AidlAnnotation& annotation : node.GetAnnotations()) {
diff --git a/aidl_language.cpp b/aidl_language.cpp
index 5ecfb60..d99f9ef 100644
--- a/aidl_language.cpp
+++ b/aidl_language.cpp
@@ -125,6 +125,7 @@
       {AidlAnnotation::Type::BACKING, "Backing", {{"type", "String"}}},
       {AidlAnnotation::Type::JAVA_PASSTHROUGH, "JavaPassthrough", {{"annotation", "String"}}},
       {AidlAnnotation::Type::JAVA_DEBUG, "JavaDebug", {}},
+      {AidlAnnotation::Type::IMMUTABLE, "Immutable", {}},
   };
   return kSchemas;
 }
@@ -269,6 +270,10 @@
   return GetAnnotation(annotations_, AidlAnnotation::Type::VINTF_STABILITY);
 }
 
+bool AidlAnnotatable::IsImmutable() const {
+  return GetAnnotation(annotations_, AidlAnnotation::Type::IMMUTABLE);
+}
+
 const AidlAnnotation* AidlAnnotatable::UnsupportedAppUsage() const {
   return GetAnnotation(annotations_, AidlAnnotation::Type::UNSUPPORTED_APP_USAGE);
 }
@@ -775,9 +780,9 @@
 }
 
 std::set<AidlAnnotation::Type> AidlParcelable::GetSupportedAnnotations() const {
-  return {AidlAnnotation::Type::VINTF_STABILITY, AidlAnnotation::Type::UNSUPPORTED_APP_USAGE,
+  return {AidlAnnotation::Type::VINTF_STABILITY,        AidlAnnotation::Type::UNSUPPORTED_APP_USAGE,
           AidlAnnotation::Type::JAVA_STABLE_PARCELABLE, AidlAnnotation::Type::HIDE,
-          AidlAnnotation::Type::JAVA_PASSTHROUGH};
+          AidlAnnotation::Type::JAVA_PASSTHROUGH,       AidlAnnotation::Type::IMMUTABLE};
 }
 
 bool AidlParcelable::CheckValid(const AidlTypenames& typenames) const {
@@ -817,9 +822,12 @@
 }
 
 std::set<AidlAnnotation::Type> AidlStructuredParcelable::GetSupportedAnnotations() const {
-  return {AidlAnnotation::Type::VINTF_STABILITY, AidlAnnotation::Type::UNSUPPORTED_APP_USAGE,
-          AidlAnnotation::Type::HIDE, AidlAnnotation::Type::JAVA_PASSTHROUGH,
-          AidlAnnotation::Type::JAVA_DEBUG};
+  return {AidlAnnotation::Type::VINTF_STABILITY,
+          AidlAnnotation::Type::UNSUPPORTED_APP_USAGE,
+          AidlAnnotation::Type::HIDE,
+          AidlAnnotation::Type::JAVA_PASSTHROUGH,
+          AidlAnnotation::Type::JAVA_DEBUG,
+          AidlAnnotation::Type::IMMUTABLE};
 }
 
 bool AidlStructuredParcelable::CheckValid(const AidlTypenames& typenames) const {
@@ -830,7 +838,11 @@
 
   for (const auto& v : GetFields()) {
     success = success && v->CheckValid(typenames);
+    if (IsImmutable()) {
+      success = success && typenames.CanBeImmutable(v->GetType());
+    }
   }
+
   return success;
 }
 
diff --git a/aidl_language.h b/aidl_language.h
index ea9fd38..512636d 100644
--- a/aidl_language.h
+++ b/aidl_language.h
@@ -166,6 +166,7 @@
     UTF8_IN_CPP,
     JAVA_PASSTHROUGH,
     JAVA_DEBUG,
+    IMMUTABLE,
   };
   static std::string TypeToString(Type type);
 
@@ -229,6 +230,7 @@
   bool IsNullable() const;
   bool IsUtf8InCpp() const;
   bool IsVintfStability() const;
+  bool IsImmutable() const;
   bool IsStableApiParcelable(Options::Language lang) const;
   bool IsHide() const;
   bool IsJavaDebug() const;
diff --git a/aidl_typenames.cpp b/aidl_typenames.cpp
index a25ad6b..4697782 100644
--- a/aidl_typenames.cpp
+++ b/aidl_typenames.cpp
@@ -217,7 +217,32 @@
   }
 }
 
-// Only T[], List, Map, ParcelFileDescriptor and Parcelable can be an out parameter.
+// Only immutable Parcelable, primitive type, and String, and List, Map, array of the types can be
+// immutable.
+bool AidlTypenames::CanBeImmutable(const AidlTypeSpecifier& type) const {
+  const string& name = type.GetName();
+  if (type.IsGeneric()) {
+    if (type.GetName() == "List" || type.GetName() == "Map") {
+      const auto& types = type.GetTypeParameters();
+      return std::all_of(types.begin(), types.end(),
+                         [this](const auto& t) { return CanBeImmutable(*t); });
+    }
+    AIDL_ERROR(type) << "For a generic type, an immutable parcelable can contain only List or Map.";
+    return false;
+  }
+  if (IsPrimitiveTypename(name) || name == "String") {
+    return true;
+  }
+  const AidlDefinedType* t = TryGetDefinedType(type.GetName());
+  if (t == nullptr) {
+    AIDL_ERROR(type) << "An immutable parcelable can contain only immutable Parcelable, primitive "
+                        "type, and String.";
+    return false;
+  }
+  return t->IsImmutable();
+}
+
+// Only T[], List, Map, ParcelFileDescriptor and mutable Parcelable can be an out parameter.
 bool AidlTypenames::CanBeOutParameter(const AidlTypeSpecifier& type) const {
   const string& name = type.GetName();
   if (IsBuiltinTypename(name) || GetEnumDeclaration(type)) {
@@ -226,7 +251,8 @@
   }
   const AidlDefinedType* t = TryGetDefinedType(type.GetName());
   CHECK(t != nullptr) << "Unrecognized type: '" << type.GetName() << "'";
-  return t->AsParcelable() != nullptr;
+  // An 'out' field is passed as an argument, so it doesn't make sense if it is immutable.
+  return t->AsParcelable() != nullptr && !t->IsImmutable();
 }
 
 const AidlEnumDeclaration* AidlTypenames::GetEnumDeclaration(const AidlTypeSpecifier& type) const {
diff --git a/aidl_typenames.h b/aidl_typenames.h
index 82001d2..2bcc409 100644
--- a/aidl_typenames.h
+++ b/aidl_typenames.h
@@ -68,6 +68,8 @@
   };
   ResolvedTypename ResolveTypename(const string& type_name) const;
   bool CanBeOutParameter(const AidlTypeSpecifier& type) const;
+  bool CanBeImmutable(const AidlTypeSpecifier& type) const;
+
   bool IsIgnorableImport(const string& import) const;
   // Returns the AidlEnumDeclaration of the given type, or nullptr if the type
   // is not an AidlEnumDeclaration;
diff --git a/aidl_unittest.cpp b/aidl_unittest.cpp
index beedbf6..6de0a1f 100644
--- a/aidl_unittest.cpp
+++ b/aidl_unittest.cpp
@@ -124,8 +124,8 @@
   {
     int _aidl_start_pos = _aidl_parcel.dataPosition();
     int _aidl_parcelable_size = _aidl_parcel.readInt();
-    if (_aidl_parcelable_size < 0) return;
     try {
+      if (_aidl_parcelable_size < 0) return;
       x = _aidl_parcel.readInt();
       if (_aidl_parcel.dataPosition() - _aidl_start_pos >= _aidl_parcelable_size) return;
       y = _aidl_parcel.readInt();
@@ -325,7 +325,8 @@
   const string method = "package a; @nullable parcelable IFoo cpp_header \"IFoo.h\";";
   const string expected_stderr =
       "ERROR: a/Foo.aidl:1.32-37: 'nullable' is not a supported annotation for this node. "
-      "It must be one of: Hide, JavaOnlyStableParcelable, UnsupportedAppUsage, VintfStability, JavaPassthrough\n";
+      "It must be one of: Hide, JavaOnlyStableParcelable, UnsupportedAppUsage, VintfStability, "
+      "JavaPassthrough, Immutable\n";
   CaptureStderr();
   EXPECT_EQ(nullptr, Parse("a/Foo.aidl", method, typenames_, GetLanguage(), &error));
   EXPECT_EQ(expected_stderr, GetCapturedStderr());
@@ -337,7 +338,8 @@
   const string method = "package a; @nullable parcelable Foo { String a; String b; }";
   const string expected_stderr =
       "ERROR: a/Foo.aidl:1.32-36: 'nullable' is not a supported annotation for this node. "
-      "It must be one of: Hide, UnsupportedAppUsage, VintfStability, JavaPassthrough, JavaDebug\n";
+      "It must be one of: Hide, UnsupportedAppUsage, VintfStability, JavaPassthrough, JavaDebug, "
+      "Immutable\n";
   CaptureStderr();
   EXPECT_EQ(nullptr, Parse("a/Foo.aidl", method, typenames_, GetLanguage(), &error));
   EXPECT_EQ(expected_stderr, GetCapturedStderr());
@@ -2279,5 +2281,36 @@
   EXPECT_EQ(AidlError::BAD_TYPE, error);
 }
 
+TEST_P(AidlTest, SupportImmutableAnnotation) {
+  io_delegate_.SetFileContents(
+      "Foo.aidl",
+      "@Immutable parcelable Foo { int a; Bar b; List<Bar> c; Map<String, Baz> d; Bar[] e; }");
+  io_delegate_.SetFileContents("Bar.aidl", "@Immutable parcelable Bar { String a; }");
+  io_delegate_.SetFileContents("Baz.aidl", "@Immutable @JavaOnlyStableParcelable parcelable Baz;");
+  Options options = Options::From("aidl --lang=java -I . Foo.aidl");
+  EXPECT_EQ(0, ::android::aidl::compile_aidl(options, io_delegate_));
+}
+
+TEST_P(AidlTest, RejectMutableParcelableFromImmutableParcelable) {
+  io_delegate_.SetFileContents("Foo.aidl", "@Immutable parcelable Foo { Bar bar; }");
+  io_delegate_.SetFileContents("Bar.aidl", "parcelable Bar { String a; }");
+  Options options = Options::From("aidl --lang=java Foo.aidl -I .");
+  EXPECT_NE(0, ::android::aidl::compile_aidl(options, io_delegate_));
+}
+
+TEST_P(AidlTest, ImmtuableParcelableCannotBeInOut) {
+  io_delegate_.SetFileContents("Foo.aidl", "@Immutable parcelable Foo { int a; }");
+  io_delegate_.SetFileContents("IBar.aidl", "interface IBar { void my(inout Foo); }");
+  Options options = Options::From("aidl --lang=java IBar.aidl -I .");
+  EXPECT_NE(0, ::android::aidl::compile_aidl(options, io_delegate_));
+}
+
+TEST_P(AidlTest, ImmtuableParcelableCannotBeOut) {
+  io_delegate_.SetFileContents("Foo.aidl", "@Immutable parcelable Foo { int a; }");
+  io_delegate_.SetFileContents("IBar.aidl", "interface IBar { void my(out Foo); }");
+  Options options = Options::From("aidl --lang=java IBar.aidl -I .");
+  EXPECT_NE(0, ::android::aidl::compile_aidl(options, io_delegate_));
+}
+
 }  // namespace aidl
 }  // namespace android
diff --git a/generate_java.cpp b/generate_java.cpp
index 3bb5ba8..4da0c4f 100644
--- a/generate_java.cpp
+++ b/generate_java.cpp
@@ -110,11 +110,12 @@
       out << a << "\n";
     }
     out << "public ";
-    if (variable->GetType().GetName() == "ParcelableHolder") {
+
+    if (variable->GetType().GetName() == "ParcelableHolder" || parcel->IsImmutable()) {
       out << "final ";
     }
     out << JavaSignatureOf(variable->GetType(), typenames) << " " << variable->GetName();
-    if (variable->GetDefaultValue()) {
+    if (!parcel->IsImmutable() && variable->GetDefaultValue()) {
       out << " = " << variable->ValueString(ConstantValueDecorator);
     } else if (variable->GetType().GetName() == "ParcelableHolder") {
       out << std::boolalpha;
@@ -145,9 +146,13 @@
   out << "  @Override\n";
   out << "  public " << parcel->GetName()
       << " createFromParcel(android.os.Parcel _aidl_source) {\n";
-  out << "    " << parcel->GetName() << " _aidl_out = new " << parcel->GetName() << "();\n";
-  out << "    _aidl_out.readFromParcel(_aidl_source);\n";
-  out << "    return _aidl_out;\n";
+  if (parcel->IsImmutable()) {
+    out << "    return new " << parcel->GetName() << "(_aidl_source);\n";
+  } else {
+    out << "    " << parcel->GetName() << " _aidl_out = new " << parcel->GetName() << "();\n";
+    out << "    _aidl_out.readFromParcel(_aidl_source);\n";
+    out << "    return _aidl_out;\n";
+  }
   out << "  }\n";
   out << "  @Override\n";
   out << "  public " << parcel->GetName() << "[] newArray(int _aidl_size) {\n";
@@ -198,20 +203,63 @@
 
   parcel_class->elements.push_back(write_method);
 
-  auto read_method = std::make_shared<Method>();
-  read_method->modifiers = PUBLIC | FINAL;
-  read_method->returnType = "void";
-  read_method->name = "readFromParcel";
-  read_method->parameters.push_back(parcel_variable);
-  read_method->statements = std::make_shared<StatementBlock>();
+  if (parcel->IsImmutable()) {
+    auto constructor = std::make_shared<Method>();
+    constructor->modifiers = PUBLIC;
+    constructor->name = parcel->GetName();
+    constructor->statements = std::make_shared<StatementBlock>();
+    for (const auto& field : parcel->GetFields()) {
+      constructor->parameters.push_back(std::make_shared<Variable>(
+          JavaSignatureOf(field->GetType(), typenames), field->GetName()));
+      out.str("");
 
+      out << "this." << field->GetName() << " = ";
+      if (field->GetType().GetName() == "List") {
+        out << "java.util.Collections.unmodifiableList(" << field->GetName() << ");\n";
+      } else if (field->GetType().GetName() == "Map") {
+        out << "java.util.Collections.unmodifiableMap(" << field->GetName() << ");\n";
+      } else {
+        out << field->GetName() << ";\n";
+      }
+      constructor->statements->Add(std::make_shared<LiteralStatement>(out.str()));
+    }
+    parcel_class->elements.push_back(constructor);
+  }
+
+  // For an immutable parcelable, generate a constructor with a parcel object,
+  // Otherwise, generate readFromParcel method.
+  auto read_or_create_method = std::make_shared<Method>();
+  if (parcel->IsImmutable()) {
+    auto constructor = std::make_shared<Method>();
+    read_or_create_method->modifiers = PUBLIC;
+    read_or_create_method->name = parcel->GetName();
+    read_or_create_method->parameters.push_back(parcel_variable);
+    read_or_create_method->statements = std::make_shared<StatementBlock>();
+  } else {
+    read_or_create_method->modifiers = PUBLIC | FINAL;
+    read_or_create_method->returnType = "void";
+    read_or_create_method->name = "readFromParcel";
+    read_or_create_method->parameters.push_back(parcel_variable);
+    read_or_create_method->statements = std::make_shared<StatementBlock>();
+  }
   out.str("");
+  if (parcel->IsImmutable()) {
+    for (const auto& field : parcel->GetFields()) {
+      out << JavaSignatureOf(field->GetType(), typenames) << " " << field->GetName();
+      if (field->GetDefaultValue()) {
+        out << " = " << field->ValueString(ConstantValueDecorator);
+      } else {
+        out << " = null";
+      }
+      out << ";\n";
+    }
+  }
   out << "int _aidl_start_pos = _aidl_parcel.dataPosition();\n"
       << "int _aidl_parcelable_size = _aidl_parcel.readInt();\n"
-      << "if (_aidl_parcelable_size < 0) return;\n"
-      << "try {\n";
+      << "try {\n"
+      << "  if (_aidl_parcelable_size < 0) return;\n";
 
-  read_method->statements->Add(std::make_shared<LiteralStatement>(out.str()));
+  read_or_create_method->statements->Add(std::make_shared<LiteralStatement>(out.str()));
 
   out.str("");
   out << "  if (_aidl_parcel.dataPosition() - _aidl_start_pos >= _aidl_parcelable_size) return;\n";
@@ -221,6 +269,8 @@
   // at most once.
   bool is_classloader_created = false;
   for (const auto& field : parcel->GetFields()) {
+    const auto field_variable_name =
+        (parcel->IsImmutable() ? "_aidl_temp_" : "") + field->GetName();
     string code;
     CodeWriterPtr writer = CodeWriter::ForString(&code);
     CodeGeneratorContext context{
@@ -228,25 +278,44 @@
         .typenames = typenames,
         .type = field->GetType(),
         .parcel = parcel_variable->name,
-        .var = field->GetName(),
+        .var = field_variable_name,
         .is_classloader_created = &is_classloader_created,
     };
     context.writer.Indent();
+    if (parcel->IsImmutable()) {
+      context.writer.Write("%s %s;\n", JavaSignatureOf(field->GetType(), typenames).c_str(),
+                           field_variable_name.c_str());
+    }
     CreateFromParcelFor(context);
+    if (parcel->IsImmutable()) {
+      context.writer.Write("%s = %s;\n", field->GetName().c_str(), field_variable_name.c_str());
+    }
     writer->Close();
-    read_method->statements->Add(std::make_shared<LiteralStatement>(code));
+    read_or_create_method->statements->Add(std::make_shared<LiteralStatement>(code));
     if (!sizeCheck) sizeCheck = std::make_shared<LiteralStatement>(out.str());
-    read_method->statements->Add(sizeCheck);
+    read_or_create_method->statements->Add(sizeCheck);
   }
 
   out.str("");
   out << "} finally {\n"
-      << "  _aidl_parcel.setDataPosition(_aidl_start_pos + _aidl_parcelable_size);\n"
-      << "}\n";
+      << "  _aidl_parcel.setDataPosition(_aidl_start_pos + _aidl_parcelable_size);\n";
+  if (parcel->IsImmutable()) {
+    for (const auto& field : parcel->GetFields()) {
+      out << "  this." << field->GetName() << " = ";
+      if (field->GetType().GetName() == "List") {
+        out << "java.util.Collections.unmodifiableList(" << field->GetName() << ");\n";
+      } else if (field->GetType().GetName() == "Map") {
+        out << "java.util.Collections.unmodifiableMap(" << field->GetName() << ");\n";
+      } else {
+        out << field->GetName() << ";\n";
+      }
+    }
+  }
+  out << "}\n";
 
-  read_method->statements->Add(std::make_shared<LiteralStatement>(out.str()));
+  read_or_create_method->statements->Add(std::make_shared<LiteralStatement>(out.str()));
 
-  parcel_class->elements.push_back(read_method);
+  parcel_class->elements.push_back(read_or_create_method);
 
   if (parcel->IsJavaDebug()) {
     out.str("");
diff --git a/tests/android/aidl/tests/immutable/Bar.aidl b/tests/android/aidl/tests/immutable/Bar.aidl
new file mode 100644
index 0000000..ac04ad0
--- /dev/null
+++ b/tests/android/aidl/tests/immutable/Bar.aidl
@@ -0,0 +1,22 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.aidl.tests.immutable;
+
+@Immutable
+parcelable Bar {
+    String s;
+}
\ No newline at end of file
diff --git a/tests/android/aidl/tests/immutable/Foo.aidl b/tests/android/aidl/tests/immutable/Foo.aidl
new file mode 100644
index 0000000..ca8ba01
--- /dev/null
+++ b/tests/android/aidl/tests/immutable/Foo.aidl
@@ -0,0 +1,28 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.aidl.tests.immutable;
+
+import android.aidl.tests.immutable.Bar;
+
+@Immutable
+parcelable Foo {
+    int a;
+    Bar b;
+    List<Bar> c;
+    Map<String, Bar> d;
+    Bar[] e;
+}
\ No newline at end of file
diff --git a/tests/android/aidl/tests/immutable/IBaz.aidl b/tests/android/aidl/tests/immutable/IBaz.aidl
new file mode 100644
index 0000000..69538e2
--- /dev/null
+++ b/tests/android/aidl/tests/immutable/IBaz.aidl
@@ -0,0 +1,23 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.aidl.tests.immutable;
+
+import android.aidl.tests.immutable.Foo;
+
+interface IBaz {
+    Foo RepeatFoo(in Foo a);
+}
\ No newline at end of file
diff --git a/tests/java_app/src/android/aidl/tests/ImmutableAnnotationTests.java b/tests/java_app/src/android/aidl/tests/ImmutableAnnotationTests.java
new file mode 100644
index 0000000..1db6b26
--- /dev/null
+++ b/tests/java_app/src/android/aidl/tests/ImmutableAnnotationTests.java
@@ -0,0 +1,61 @@
+/*
+ * Copyright (C) 2020, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.aidl.tests;
+
+import static org.junit.Assert.fail;
+
+import android.aidl.tests.immutable.Bar;
+import android.aidl.tests.immutable.Foo;
+import android.os.RemoteException;
+import java.lang.UnsupportedOperationException;
+import java.lang.reflect.Field;
+import java.lang.reflect.Modifier;
+import java.util.ArrayList;
+import java.util.HashMap;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+@RunWith(JUnit4.class)
+public class ImmutableAnnotationTests {
+  @Test
+  public void testEveryFieldIsFinal() throws RemoteException {
+    for (Field f : Foo.class.getDeclaredFields()) {
+      if (!Modifier.isFinal(f.getModifiers())) {
+        fail(f.getName() + " should be final.");
+      }
+    }
+  }
+
+  @Test(expected = UnsupportedOperationException.class)
+  public void testListIsUnmodifiable() throws RemoteException {
+    Foo foo =
+        new Foo(7, new Bar("my"), new ArrayList<Bar>(), new HashMap<String, Bar>(), new Bar[5]);
+    foo.c.add(new Bar("hi"));
+    // It is supposed to fail.
+    fail("A List in an immutable parcelable isn't modifiable.");
+  }
+
+  @Test(expected = UnsupportedOperationException.class)
+  public void testMapIsUnmodifiable() throws RemoteException {
+    Foo foo =
+        new Foo(7, new Bar("my"), new ArrayList<Bar>(), new HashMap<String, Bar>(), new Bar[5]);
+    foo.d.put("key", new Bar("value"));
+    // It is supposed to fail.
+    fail("A Map in an immutable parcelable isn't modifiable.");
+  }
+}