Add support for List<String>

Bug: 24470786
Test: expanded unit tests pass, integration test passes

Change-Id: Ie85e43c04e214315f42a9f1ae83e6b4c67c2f081
diff --git a/generate_cpp_unittest.cpp b/generate_cpp_unittest.cpp
index f5518f5..af2fe23 100644
--- a/generate_cpp_unittest.cpp
+++ b/generate_cpp_unittest.cpp
@@ -43,6 +43,7 @@
   int[] Send(in int[] goes_in, inout double[] goes_in_and_out, out boolean[] goes_out);
   oneway void Piff(int times);
   IFooType TakesABinder(IFooType f);
+  List<String> StringListMethod(in java.util.List<String> input, out List<String> output);
 })";
 
 const char kExpectedComplexTypeClientHeaderOutput[] =
@@ -65,6 +66,7 @@
 android::status_t Send(const std::vector<int32_t>& goes_in, std::vector<double>* goes_in_and_out, std::vector<bool>* goes_out, std::vector<int32_t>* _aidl_return) override;
 android::status_t Piff(int32_t times) override;
 android::status_t TakesABinder(const android::sp<::foo::IFooType>& f, android::sp<::foo::IFooType>* _aidl_return) override;
+android::status_t StringListMethod(const std::vector<android::String16>& input, std::vector<android::String16>* output, std::vector<android::String16>* _aidl_return) override;
 };  // class BpComplexTypeInterface
 
 }  // namespace os
@@ -170,6 +172,37 @@
 return status;
 }
 
+android::status_t BpComplexTypeInterface::StringListMethod(const std::vector<android::String16>& input, std::vector<android::String16>* output, std::vector<android::String16>* _aidl_return) {
+android::Parcel data;
+android::Parcel reply;
+android::status_t status;
+status = data.writeInterfaceToken(getInterfaceDescriptor());
+if (((status) != (android::OK))) {
+return status;
+}
+status = data.writeString16Vector(input);
+if (((status) != (android::OK))) {
+return status;
+}
+status = remote()->transact(IComplexTypeInterface::STRINGLISTMETHOD, data, &reply);
+if (((status) != (android::OK))) {
+return status;
+}
+if (reply.readExceptionCode()) {
+status = android::FAILED_TRANSACTION;
+return status;
+}
+status = reply.readString16Vector(_aidl_return);
+if (((status) != (android::OK))) {
+return status;
+}
+status = reply.readString16Vector(output);
+if (((status) != (android::OK))) {
+return status;
+}
+return status;
+}
+
 }  // namespace os
 
 }  // namespace android
@@ -295,6 +328,37 @@
 }
 }
 break;
+case Call::STRINGLISTMETHOD:
+{
+std::vector<android::String16> in_input;
+std::vector<android::String16> out_output;
+std::vector<android::String16> _aidl_return;
+if ((!data.checkInterface(this))) {
+status = android::BAD_TYPE;
+break;
+}
+status = data.readString16Vector(&in_input);
+if (((status) != (android::OK))) {
+break;
+}
+status = StringListMethod(in_input, &out_output, &_aidl_return);
+if (((status) != (android::OK))) {
+break;
+}
+status = reply->writeNoException();
+if (((status) != (android::OK))) {
+break;
+}
+status = reply->writeString16Vector(_aidl_return);
+if (((status) != (android::OK))) {
+break;
+}
+status = reply->writeString16Vector(out_output);
+if (((status) != (android::OK))) {
+break;
+}
+}
+break;
 default:
 {
 status = android::BBinder::onTransact(code, data, reply, flags);
@@ -317,6 +381,7 @@
 #include <binder/IInterface.h>
 #include <cstdint>
 #include <foo/IFooType.h>
+#include <utils/String16.h>
 #include <utils/StrongPointer.h>
 #include <vector>
 
@@ -330,10 +395,12 @@
 virtual android::status_t Send(const std::vector<int32_t>& goes_in, std::vector<double>* goes_in_and_out, std::vector<bool>* goes_out, std::vector<int32_t>* _aidl_return) = 0;
 virtual android::status_t Piff(int32_t times) = 0;
 virtual android::status_t TakesABinder(const android::sp<::foo::IFooType>& f, android::sp<::foo::IFooType>* _aidl_return) = 0;
+virtual android::status_t StringListMethod(const std::vector<android::String16>& input, std::vector<android::String16>* output, std::vector<android::String16>* _aidl_return) = 0;
 enum Call {
   SEND = android::IBinder::FIRST_CALL_TRANSACTION + 0,
   PIFF = android::IBinder::FIRST_CALL_TRANSACTION + 1,
   TAKESABINDER = android::IBinder::FIRST_CALL_TRANSACTION + 2,
+  STRINGLISTMETHOD = android::IBinder::FIRST_CALL_TRANSACTION + 3,
 };
 };  // class IComplexTypeInterface
 
diff --git a/tests/aidl_test_client.cpp b/tests/aidl_test_client.cpp
index b5f800c..23ca80d 100644
--- a/tests/aidl_test_client.cpp
+++ b/tests/aidl_test_client.cpp
@@ -153,6 +153,16 @@
   return true;
 }
 
+bool ConfirmReverseLists(const sp<ITestService>& s) {
+  cout << "Confirming passing and returning List<T> works." << endl;
+
+  if (!ReverseArray(s, &ITestService::ReverseStringList,
+                    {String16{"f"}, String16{"a"}, String16{"b"}})) {
+    return false;
+  }
+
+  return true;
+}
 }  // namespace
 
 int main(int /* argc */, char * /* argv */ []) {
@@ -164,5 +174,7 @@
 
   if (!ConfirmReverseArrays(service)) return 1;
 
+  if (!ConfirmReverseLists(service)) return 1;
+
   return 0;
 }
diff --git a/tests/aidl_test_service.cpp b/tests/aidl_test_service.cpp
index 0e8a260..ca21b5d 100644
--- a/tests/aidl_test_service.cpp
+++ b/tests/aidl_test_service.cpp
@@ -252,6 +252,12 @@
     return err;
   }
 
+  status_t ReverseStringList(const vector<String16>& input,
+                             vector<String16>* repeated,
+                             vector<String16>* _aidl_return) override {
+    return ReverseArray(input, repeated, _aidl_return);
+  }
+
  private:
   map<String16, sp<INamedCallback>> service_map_;
 };
diff --git a/tests/android/aidl/tests/ITestService.aidl b/tests/android/aidl/tests/ITestService.aidl
index bb992ac..5793139 100644
--- a/tests/android/aidl/tests/ITestService.aidl
+++ b/tests/android/aidl/tests/ITestService.aidl
@@ -39,6 +39,11 @@
   double[]  ReverseDouble (in double[]  input, out double[]  repeated);
   String[]  ReverseString (in String[]  input, out String[]  repeated);
 
+  // Test that clients can send and receive Binders.
   INamedCallback GetOtherTestService(String name);
   boolean VerifyName(INamedCallback service, String name);
+
+  // Test that List<T> types work correctly.
+  List<String> ReverseStringList(in List<String> input,
+                                 out List<String> repeated);
 }
diff --git a/tests/java_app/src/android/aidl/tests/TestServiceClient.java b/tests/java_app/src/android/aidl/tests/TestServiceClient.java
index 2aad221..6388aed 100644
--- a/tests/java_app/src/android/aidl/tests/TestServiceClient.java
+++ b/tests/java_app/src/android/aidl/tests/TestServiceClient.java
@@ -28,6 +28,9 @@
 import java.io.PrintWriter;
 import java.io.FileOutputStream;
 import java.util.Arrays;
+import java.util.List;
+import java.util.Collections;
+import java.util.ArrayList;
 
 // Generated
 import android.aidl.tests.ITestService;
@@ -366,6 +369,29 @@
       mLog.log("...Exchange of binders works");
     }
 
+    private void checkListReversal(ITestService service)
+            throws TestFailException {
+        mLog.log("Checking that service can reverse and return lists...");
+        try {
+            {
+                List<String> input = Arrays.asList("Walk", "into", "Córdoba");
+                List<String> echoed = new ArrayList<String>();
+                List<String> reversed = service.ReverseStringList(input, echoed);
+                if (!input.equals(echoed)) {
+                    mLog.logAndThrow("Failed to echo input List<String> back.");
+                }
+                Collections.reverse(input);
+                if (!input.equals(reversed)) {
+                    mLog.logAndThrow("Reversed list is not correct.");
+                }
+            }
+        } catch (RemoteException ex) {
+            mLog.log(ex.toString());
+            mLog.logAndThrow("Service failed to reverse an List<String>.");
+        }
+        mLog.log("...service can reverse and return lists.");
+    }
+
     @Override
     protected void onCreate(Bundle savedInstanceState) {
         super.onCreate(savedInstanceState);
diff --git a/type_cpp.cpp b/type_cpp.cpp
index 0cf4767..06bcf2a 100644
--- a/type_cpp.cpp
+++ b/type_cpp.cpp
@@ -110,6 +110,28 @@
   }
 };
 
+class StringListType : public Type {
+ public:
+  StringListType()
+      : Type(ValidatableType::KIND_BUILT_IN, "java.util", "List<String>",
+            "utils/String16.h", "std::vector<android::String16>",
+             "readString16Vector", "writeString16Vector") {}
+  virtual ~StringListType() = default;
+  bool CanBeOutParameter() const override { return true; }
+
+  void GetHeaders(bool is_array, set<string>* headers) const {
+    if (is_array) {
+      LOG(FATAL) << "Type checking did not catch that List<String> "
+                    "was marked as array";
+    }
+    Type::GetHeaders(is_array, headers);
+    headers->insert("vector");
+  }
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(StringListType);
+};  // class StringListType
+
 }  // namespace
 
 Type::Type(int kind,
@@ -133,7 +155,6 @@
       parcel_write_array_method_(write_array_method) {}
 
 bool Type::CanBeArray() const { return ! parcel_read_array_method_.empty(); }
-bool Type::CanBeOutParameter() const { return false; }
 bool Type::CanWriteToParcel() const { return true; }
 void Type::GetHeaders(bool is_array, set<string>* headers) const {
   if (!header_.empty()) {
@@ -199,10 +220,11 @@
       kNoHeader, "char16_t", "readChar", "writeChar",
       "readCharVector", "writeCharVector"));
 
-  Add(new Type(ValidatableType::KIND_BUILT_IN, kNoPackage, "String",
-               "utils/String16.h", "android::String16",
-               "readString16", "writeString16", "readString16Vector",
-               "writeString16Vector"));
+  string_type_ = new Type(ValidatableType::KIND_BUILT_IN, kNoPackage, "String",
+                          "utils/String16.h", "android::String16",
+                          "readString16", "writeString16",
+                          "readString16Vector", "writeString16Vector");
+  Add(string_type_);
 
   void_type_ = new class VoidType();
   Add(void_type_);
@@ -221,9 +243,28 @@
   return true;
 }
 
-bool TypeNamespace::AddListType(const std::string& /* type_name */) {
-  // TODO Support list types b/24470786
-  LOG(ERROR) << "Passing lists is unimplemented in C++ generation.";
+bool TypeNamespace::AddListType(const std::string& type_name) {
+  const Type* contained_type = Find(type_name);
+  if (!contained_type) {
+    LOG(ERROR) << "Cannot create List<" << type_name << "> because contained "
+                  "type cannot be found or is invalid.";
+    return false;
+  }
+  if (contained_type->IsCppPrimitive()) {
+    LOG(ERROR) << "Cannot create List<" << type_name << "> because contained "
+                  "type is a primitive in Java and Java List cannot hold "
+                  "primitives.";
+    return false;
+  }
+
+  if (contained_type == StringType()) {
+    Add(new StringListType());
+    return true;
+  }
+  // TODO Support lists of parcelables b/23600712
+  // TODO Support lists of binders b/24470875
+
+  LOG(ERROR) << "aidl-cpp does not yet support List<" << type_name << ">";
   return false;
 }
 
diff --git a/type_cpp.h b/type_cpp.h
index 98eb211..4226f9f 100644
--- a/type_cpp.h
+++ b/type_cpp.h
@@ -47,7 +47,7 @@
 
   // overrides of ValidatableType
   bool CanBeArray() const override;
-  bool CanBeOutParameter() const override;
+  bool CanBeOutParameter() const override { return false; }
   bool CanWriteToParcel() const override;
 
   std::string CppType(bool is_array) const;
@@ -84,7 +84,6 @@
   DISALLOW_COPY_AND_ASSIGN(PrimitiveType);
 };  // class PrimitiveType
 
-
 class TypeNamespace : public ::android::aidl::LanguageTypeNamespace<Type> {
  public:
   TypeNamespace() = default;
@@ -105,9 +104,11 @@
                   const std::string& filename) const override;
 
   const Type* VoidType() const { return void_type_; }
+  const Type* StringType() const { return string_type_; }
 
  private:
   Type* void_type_ = nullptr;
+  Type* string_type_ = nullptr;
 
   DISALLOW_COPY_AND_ASSIGN(TypeNamespace);
 };  // class TypeNamespace
diff --git a/type_cpp_unittest.cpp b/type_cpp_unittest.cpp
index d4232a0..ae5ffc8 100644
--- a/type_cpp_unittest.cpp
+++ b/type_cpp_unittest.cpp
@@ -43,6 +43,12 @@
   EXPECT_NE(types_.Find("String"), nullptr);
 }
 
+TEST_F(CppTypeNamespaceTest, SupportsListString) {
+  EXPECT_EQ(types_.Find("List<String>"), nullptr);
+  EXPECT_TRUE(types_.AddListType("String"));
+  EXPECT_NE(types_.Find("List<String>"), nullptr);
+}
+
 }  // namespace cpp
 }  // namespace android
 }  // namespace aidl