1 /* 2 * Copyright (C) 2015, The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 #include "aidl.h" 18 19 #include <fcntl.h> 20 #include <iostream> 21 #include <map> 22 #include <stdio.h> 23 #include <stdlib.h> 24 #include <string.h> 25 #include <sys/param.h> 26 #include <sys/stat.h> 27 #include <unistd.h> 28 29 #ifdef _WIN32 30 #include <io.h> 31 #include <direct.h> 32 #include <sys/stat.h> 33 #endif 34 35 #include <android-base/strings.h> 36 37 #include "aidl_language.h" 38 #include "generate_cpp.h" 39 #include "generate_java.h" 40 #include "import_resolver.h" 41 #include "logging.h" 42 #include "options.h" 43 #include "os.h" 44 #include "type_cpp.h" 45 #include "type_java.h" 46 #include "type_namespace.h" 47 48 #ifndef O_BINARY 49 # define O_BINARY 0 50 #endif 51 52 using android::base::Join; 53 using android::base::Split; 54 using std::cerr; 55 using std::endl; 56 using std::map; 57 using std::set; 58 using std::string; 59 using std::unique_ptr; 60 using std::vector; 61 62 namespace android { 63 namespace aidl { 64 namespace { 65 66 // The following are gotten as the offset from the allowable id's between 67 // android.os.IBinder.FIRST_CALL_TRANSACTION=1 and 68 // android.os.IBinder.LAST_CALL_TRANSACTION=16777215 69 const int kMinUserSetMethodId = 0; 70 const int kMaxUserSetMethodId = 16777214; 71 72 bool check_filename(const std::string& filename, 73 const std::string& package, 74 const std::string& name, 75 unsigned line) { 76 const char* p; 77 string expected; 78 string fn; 79 size_t len; 80 bool valid = false; 81 82 if (!IoDelegate::GetAbsolutePath(filename, &fn)) { 83 return false; 84 } 85 86 if (!package.empty()) { 87 expected = package; 88 expected += '.'; 89 } 90 91 len = expected.length(); 92 for (size_t i=0; i<len; i++) { 93 if (expected[i] == '.') { 94 expected[i] = OS_PATH_SEPARATOR; 95 } 96 } 97 98 expected.append(name, 0, name.find('.')); 99 100 expected += ".aidl"; 101 102 len = fn.length(); 103 valid = (len >= expected.length()); 104 105 if (valid) { 106 p = fn.c_str() + (len - expected.length()); 107 108 #ifdef _WIN32 109 if (OS_PATH_SEPARATOR != '/') { 110 // Input filename under cygwin most likely has / separators 111 // whereas the expected string uses \\ separators. Adjust 112 // them accordingly. 113 for (char *c = const_cast<char *>(p); *c; ++c) { 114 if (*c == '/') *c = OS_PATH_SEPARATOR; 115 } 116 } 117 #endif 118 119 // aidl assumes case-insensitivity on Mac Os and Windows. 120 #if defined(__linux__) 121 valid = (expected == p); 122 #else 123 valid = !strcasecmp(expected.c_str(), p); 124 #endif 125 } 126 127 if (!valid) { 128 fprintf(stderr, "%s:%d interface %s should be declared in a file" 129 " called %s.\n", 130 filename.c_str(), line, name.c_str(), expected.c_str()); 131 } 132 133 return valid; 134 } 135 136 bool check_filenames(const std::string& filename, const AidlDocument* doc) { 137 if (!doc) 138 return true; 139 140 const AidlInterface* interface = doc->GetInterface(); 141 142 if (interface) { 143 return check_filename(filename, interface->GetPackage(), 144 interface->GetName(), interface->GetLine()); 145 } 146 147 bool success = true; 148 149 for (const auto& item : doc->GetParcelables()) { 150 success &= check_filename(filename, item->GetPackage(), item->GetName(), 151 item->GetLine()); 152 } 153 154 return success; 155 } 156 157 bool gather_types(const std::string& filename, 158 const AidlDocument* doc, 159 TypeNamespace* types) { 160 bool success = true; 161 162 const AidlInterface* interface = doc->GetInterface(); 163 164 if (interface) 165 return types->AddBinderType(*interface, filename); 166 167 for (const auto& item : doc->GetParcelables()) { 168 success &= types->AddParcelableType(*item, filename); 169 } 170 171 return success; 172 } 173 174 int check_types(const string& filename, 175 const AidlInterface* c, 176 TypeNamespace* types) { 177 int err = 0; 178 179 if (c->IsUtf8() && c->IsUtf8InCpp()) { 180 cerr << filename << ":" << c->GetLine() 181 << "Interface cannot be marked as both @utf8 and @utf8InCpp"; 182 err = 1; 183 } 184 185 // Has to be a pointer due to deleting copy constructor. No idea why. 186 map<string, const AidlMethod*> method_names; 187 for (const auto& m : c->GetMethods()) { 188 bool oneway = m->IsOneway() || c->IsOneway(); 189 190 if (!types->MaybeAddContainerType(m->GetType())) { 191 err = 1; // return type is invalid 192 } 193 194 const ValidatableType* return_type = 195 types->GetReturnType(m->GetType(), filename, *c); 196 197 if (!return_type) { 198 err = 1; 199 } 200 201 m->GetMutableType()->SetLanguageType(return_type); 202 203 if (oneway && m->GetType().GetName() != "void") { 204 cerr << filename << ":" << m->GetLine() 205 << " oneway method '" << m->GetName() << "' cannot return a value" 206 << endl; 207 err = 1; 208 } 209 210 int index = 1; 211 for (const auto& arg : m->GetArguments()) { 212 if (!types->MaybeAddContainerType(arg->GetType())) { 213 err = 1; 214 } 215 216 const ValidatableType* arg_type = 217 types->GetArgType(*arg, index, filename, *c); 218 219 if (!arg_type) { 220 err = 1; 221 } 222 223 arg->GetMutableType()->SetLanguageType(arg_type); 224 225 if (oneway && arg->IsOut()) { 226 cerr << filename << ":" << m->GetLine() 227 << " oneway method '" << m->GetName() 228 << "' cannot have out parameters" << endl; 229 err = 1; 230 } 231 } 232 233 auto it = method_names.find(m->GetName()); 234 // prevent duplicate methods 235 if (it == method_names.end()) { 236 method_names[m->GetName()] = m.get(); 237 } else { 238 cerr << filename << ":" << m->GetLine() 239 << " attempt to redefine method " << m->GetName() << "," << endl 240 << filename << ":" << it->second->GetLine() 241 << " previously defined here." << endl; 242 err = 1; 243 } 244 } 245 return err; 246 } 247 248 void write_common_dep_file(const string& output_file, 249 const vector<string>& aidl_sources, 250 CodeWriter* writer, 251 const bool ninja) { 252 // Encode that the output file depends on aidl input files. 253 writer->Write("%s : \\\n", output_file.c_str()); 254 writer->Write(" %s", Join(aidl_sources, " \\\n ").c_str()); 255 writer->Write("\n"); 256 257 if (!ninja) { 258 writer->Write("\n"); 259 // Output "<input_aidl_file>: " so make won't fail if the input .aidl file 260 // has been deleted, moved or renamed in incremental build. 261 for (const auto& src : aidl_sources) { 262 writer->Write("%s :\n", src.c_str()); 263 } 264 } 265 } 266 267 bool write_java_dep_file(const JavaOptions& options, 268 const vector<unique_ptr<AidlImport>>& imports, 269 const IoDelegate& io_delegate, 270 const string& output_file_name) { 271 string dep_file_name = options.DependencyFilePath(); 272 if (dep_file_name.empty()) { 273 return true; // nothing to do 274 } 275 CodeWriterPtr writer = io_delegate.GetCodeWriter(dep_file_name); 276 if (!writer) { 277 LOG(ERROR) << "Could not open dependency file: " << dep_file_name; 278 return false; 279 } 280 281 vector<string> source_aidl = {options.input_file_name_}; 282 for (const auto& import : imports) { 283 if (!import->GetFilename().empty()) { 284 source_aidl.push_back(import->GetFilename()); 285 } 286 } 287 288 write_common_dep_file(output_file_name, source_aidl, writer.get(), 289 options.DependencyFileNinja()); 290 291 return true; 292 } 293 294 bool write_cpp_dep_file(const CppOptions& options, 295 const AidlInterface& interface, 296 const vector<unique_ptr<AidlImport>>& imports, 297 const IoDelegate& io_delegate) { 298 using ::android::aidl::cpp::HeaderFile; 299 using ::android::aidl::cpp::ClassNames; 300 301 string dep_file_name = options.DependencyFilePath(); 302 if (dep_file_name.empty()) { 303 return true; // nothing to do 304 } 305 CodeWriterPtr writer = io_delegate.GetCodeWriter(dep_file_name); 306 if (!writer) { 307 LOG(ERROR) << "Could not open dependency file: " << dep_file_name; 308 return false; 309 } 310 311 vector<string> source_aidl = {options.InputFileName()}; 312 for (const auto& import : imports) { 313 if (!import->GetFilename().empty()) { 314 source_aidl.push_back(import->GetFilename()); 315 } 316 } 317 318 write_common_dep_file(options.OutputCppFilePath(), source_aidl, writer.get(), 319 options.DependencyFileNinja()); 320 321 if (!options.DependencyFileNinja()) { 322 vector<string> headers; 323 for (ClassNames c : {ClassNames::CLIENT, 324 ClassNames::SERVER, 325 ClassNames::INTERFACE}) { 326 headers.push_back(options.OutputHeaderDir() + '/' + 327 HeaderFile(interface, c, false /* use_os_sep */)); 328 } 329 330 writer->Write("\n"); 331 332 // Generated headers also depend on the source aidl files. 333 writer->Write("%s : \\\n %s\n", Join(headers, " \\\n ").c_str(), 334 Join(source_aidl, " \\\n ").c_str()); 335 } 336 337 return true; 338 } 339 340 string generate_outputFileName(const JavaOptions& options, 341 const AidlInterface& interface) { 342 const string& name = interface.GetName(); 343 string package = interface.GetPackage(); 344 string result; 345 346 // create the path to the destination folder based on the 347 // interface package name 348 result = options.output_base_folder_; 349 result += OS_PATH_SEPARATOR; 350 351 string packageStr = package; 352 size_t len = packageStr.length(); 353 for (size_t i=0; i<len; i++) { 354 if (packageStr[i] == '.') { 355 packageStr[i] = OS_PATH_SEPARATOR; 356 } 357 } 358 359 result += packageStr; 360 361 // add the filename by replacing the .aidl extension to .java 362 result += OS_PATH_SEPARATOR; 363 result.append(name, 0, name.find('.')); 364 result += ".java"; 365 366 return result; 367 } 368 369 int check_and_assign_method_ids(const char * filename, 370 const std::vector<std::unique_ptr<AidlMethod>>& items) { 371 // Check whether there are any methods with manually assigned id's and any that are not. 372 // Either all method id's must be manually assigned or all of them must not. 373 // Also, check for duplicates of user set id's and that the id's are within the proper bounds. 374 set<int> usedIds; 375 bool hasUnassignedIds = false; 376 bool hasAssignedIds = false; 377 for (const auto& item : items) { 378 if (item->HasId()) { 379 hasAssignedIds = true; 380 // Ensure that the user set id is not duplicated. 381 if (usedIds.find(item->GetId()) != usedIds.end()) { 382 // We found a duplicate id, so throw an error. 383 fprintf(stderr, 384 "%s:%d Found duplicate method id (%d) for method: %s\n", 385 filename, item->GetLine(), 386 item->GetId(), item->GetName().c_str()); 387 return 1; 388 } 389 // Ensure that the user set id is within the appropriate limits 390 if (item->GetId() < kMinUserSetMethodId || 391 item->GetId() > kMaxUserSetMethodId) { 392 fprintf(stderr, "%s:%d Found out of bounds id (%d) for method: %s\n", 393 filename, item->GetLine(), 394 item->GetId(), item->GetName().c_str()); 395 fprintf(stderr, " Value for id must be between %d and %d inclusive.\n", 396 kMinUserSetMethodId, kMaxUserSetMethodId); 397 return 1; 398 } 399 usedIds.insert(item->GetId()); 400 } else { 401 hasUnassignedIds = true; 402 } 403 if (hasAssignedIds && hasUnassignedIds) { 404 fprintf(stderr, 405 "%s: You must either assign id's to all methods or to none of them.\n", 406 filename); 407 return 1; 408 } 409 } 410 411 // In the case that all methods have unassigned id's, set a unique id for them. 412 if (hasUnassignedIds) { 413 int newId = 0; 414 for (const auto& item : items) { 415 item->SetId(newId++); 416 } 417 } 418 419 // success 420 return 0; 421 } 422 423 bool validate_constants(const AidlInterface& interface) { 424 bool success = true; 425 set<string> names; 426 for (const std::unique_ptr<AidlIntConstant>& int_constant : 427 interface.GetIntConstants()) { 428 if (names.count(int_constant->GetName()) > 0) { 429 LOG(ERROR) << "Found duplicate constant name '" << int_constant->GetName() 430 << "'"; 431 success = false; 432 } 433 names.insert(int_constant->GetName()); 434 // We've logged an error message for this on object construction. 435 success = success && int_constant->IsValid(); 436 } 437 for (const std::unique_ptr<AidlStringConstant>& string_constant : 438 interface.GetStringConstants()) { 439 if (names.count(string_constant->GetName()) > 0) { 440 LOG(ERROR) << "Found duplicate constant name '" << string_constant->GetName() 441 << "'"; 442 success = false; 443 } 444 names.insert(string_constant->GetName()); 445 // We've logged an error message for this on object construction. 446 success = success && string_constant->IsValid(); 447 } 448 return success; 449 } 450 451 // TODO: Remove this in favor of using the YACC parser b/25479378 452 bool ParsePreprocessedLine(const string& line, string* decl, 453 vector<string>* package, string* class_name) { 454 // erase all trailing whitespace and semicolons 455 const size_t end = line.find_last_not_of(" ;\t"); 456 if (end == string::npos) { 457 return false; 458 } 459 if (line.rfind(';', end) != string::npos) { 460 return false; 461 } 462 463 decl->clear(); 464 string type; 465 vector<string> pieces = Split(line.substr(0, end + 1), " \t"); 466 for (const string& piece : pieces) { 467 if (piece.empty()) { 468 continue; 469 } 470 if (decl->empty()) { 471 *decl = std::move(piece); 472 } else if (type.empty()) { 473 type = std::move(piece); 474 } else { 475 return false; 476 } 477 } 478 479 // Note that this logic is absolutely wrong. Given a parcelable 480 // org.some.Foo.Bar, the class name is Foo.Bar, but this code will claim that 481 // the class is just Bar. However, this was the way it was done in the past. 482 // 483 // See b/17415692 484 size_t dot_pos = type.rfind('.'); 485 if (dot_pos != string::npos) { 486 *class_name = type.substr(dot_pos + 1); 487 *package = Split(type.substr(0, dot_pos), "."); 488 } else { 489 *class_name = type; 490 package->clear(); 491 } 492 493 return true; 494 } 495 496 } // namespace 497 498 namespace internals { 499 500 bool parse_preprocessed_file(const IoDelegate& io_delegate, 501 const string& filename, TypeNamespace* types) { 502 bool success = true; 503 unique_ptr<LineReader> line_reader = io_delegate.GetLineReader(filename); 504 if (!line_reader) { 505 LOG(ERROR) << "cannot open preprocessed file: " << filename; 506 success = false; 507 return success; 508 } 509 510 string line; 511 unsigned lineno = 1; 512 for ( ; line_reader->ReadLine(&line); ++lineno) { 513 if (line.empty() || line.compare(0, 2, "//") == 0) { 514 // skip comments and empty lines 515 continue; 516 } 517 518 string decl; 519 vector<string> package; 520 string class_name; 521 if (!ParsePreprocessedLine(line, &decl, &package, &class_name)) { 522 success = false; 523 break; 524 } 525 526 if (decl == "parcelable") { 527 AidlParcelable doc(new AidlQualifiedName(class_name, ""), 528 lineno, package); 529 types->AddParcelableType(doc, filename); 530 } else if (decl == "interface") { 531 auto temp = new std::vector<std::unique_ptr<AidlMember>>(); 532 AidlInterface doc(class_name, lineno, "", false, temp, package); 533 types->AddBinderType(doc, filename); 534 } else { 535 success = false; 536 break; 537 } 538 } 539 if (!success) { 540 LOG(ERROR) << filename << ':' << lineno 541 << " malformed preprocessed file line: '" << line << "'"; 542 } 543 544 return success; 545 } 546 547 AidlError load_and_validate_aidl( 548 const std::vector<std::string>& preprocessed_files, 549 const std::vector<std::string>& import_paths, 550 const std::string& input_file_name, 551 const bool generate_traces, 552 const IoDelegate& io_delegate, 553 TypeNamespace* types, 554 std::unique_ptr<AidlInterface>* returned_interface, 555 std::vector<std::unique_ptr<AidlImport>>* returned_imports) { 556 AidlError err = AidlError::OK; 557 558 std::map<AidlImport*,std::unique_ptr<AidlDocument>> docs; 559 560 // import the preprocessed file 561 for (const string& s : preprocessed_files) { 562 if (!parse_preprocessed_file(io_delegate, s, types)) { 563 err = AidlError::BAD_PRE_PROCESSED_FILE; 564 } 565 } 566 if (err != AidlError::OK) { 567 return err; 568 } 569 570 // parse the input file 571 Parser p{io_delegate}; 572 if (!p.ParseFile(input_file_name)) { 573 return AidlError::PARSE_ERROR; 574 } 575 576 AidlDocument* parsed_doc = p.GetDocument(); 577 578 unique_ptr<AidlInterface> interface(parsed_doc->ReleaseInterface()); 579 580 if (!interface) { 581 LOG(ERROR) << "refusing to generate code from aidl file defining " 582 "parcelable"; 583 return AidlError::FOUND_PARCELABLE; 584 } 585 586 if (!check_filename(input_file_name.c_str(), interface->GetPackage(), 587 interface->GetName(), interface->GetLine()) || 588 !types->IsValidPackage(interface->GetPackage())) { 589 LOG(ERROR) << "Invalid package declaration '" << interface->GetPackage() 590 << "'"; 591 return AidlError::BAD_PACKAGE; 592 } 593 594 // parse the imports of the input file 595 ImportResolver import_resolver{io_delegate, import_paths}; 596 for (auto& import : p.GetImports()) { 597 if (types->HasImportType(*import)) { 598 // There are places in the Android tree where an import doesn't resolve, 599 // but we'll pick the type up through the preprocessed types. 600 // This seems like an error, but legacy support demands we support it... 601 continue; 602 } 603 string import_path = import_resolver.FindImportFile(import->GetNeededClass()); 604 if (import_path.empty()) { 605 cerr << import->GetFileFrom() << ":" << import->GetLine() 606 << ": couldn't find import for class " 607 << import->GetNeededClass() << endl; 608 err = AidlError::BAD_IMPORT; 609 continue; 610 } 611 import->SetFilename(import_path); 612 613 Parser p{io_delegate}; 614 if (!p.ParseFile(import->GetFilename())) { 615 cerr << "error while parsing import for class " 616 << import->GetNeededClass() << endl; 617 err = AidlError::BAD_IMPORT; 618 continue; 619 } 620 621 std::unique_ptr<AidlDocument> document(p.ReleaseDocument()); 622 if (!check_filenames(import->GetFilename(), document.get())) 623 err = AidlError::BAD_IMPORT; 624 docs[import.get()] = std::move(document); 625 } 626 if (err != AidlError::OK) { 627 return err; 628 } 629 630 // gather the types that have been declared 631 if (!types->AddBinderType(*interface.get(), input_file_name)) { 632 err = AidlError::BAD_TYPE; 633 } 634 635 interface->SetLanguageType(types->GetInterfaceType(*interface)); 636 637 interface->SetGenerateTraces(generate_traces); 638 639 for (const auto& import : p.GetImports()) { 640 // If we skipped an unresolved import above (see comment there) we'll have 641 // an empty bucket here. 642 const auto import_itr = docs.find(import.get()); 643 if (import_itr == docs.cend()) { 644 continue; 645 } 646 647 if (!gather_types(import->GetFilename(), import_itr->second.get(), types)) { 648 err = AidlError::BAD_TYPE; 649 } 650 } 651 652 // check the referenced types in parsed_doc to make sure we've imported them 653 if (check_types(input_file_name, interface.get(), types) != 0) { 654 err = AidlError::BAD_TYPE; 655 } 656 if (err != AidlError::OK) { 657 return err; 658 } 659 660 661 // assign method ids and validate. 662 if (check_and_assign_method_ids(input_file_name.c_str(), 663 interface->GetMethods()) != 0) { 664 return AidlError::BAD_METHOD_ID; 665 } 666 if (!validate_constants(*interface)) { 667 return AidlError::BAD_CONSTANTS; 668 } 669 670 if (returned_interface) 671 *returned_interface = std::move(interface); 672 673 if (returned_imports) 674 p.ReleaseImports(returned_imports); 675 676 return AidlError::OK; 677 } 678 679 } // namespace internals 680 681 int compile_aidl_to_cpp(const CppOptions& options, 682 const IoDelegate& io_delegate) { 683 unique_ptr<AidlInterface> interface; 684 std::vector<std::unique_ptr<AidlImport>> imports; 685 unique_ptr<cpp::TypeNamespace> types(new cpp::TypeNamespace()); 686 types->Init(); 687 AidlError err = internals::load_and_validate_aidl( 688 std::vector<std::string>{}, // no preprocessed files 689 options.ImportPaths(), 690 options.InputFileName(), 691 options.ShouldGenTraces(), 692 io_delegate, 693 types.get(), 694 &interface, 695 &imports); 696 if (err != AidlError::OK) { 697 return 1; 698 } 699 700 if (!write_cpp_dep_file(options, *interface, imports, io_delegate)) { 701 return 1; 702 } 703 704 return (cpp::GenerateCpp(options, *types, *interface, io_delegate)) ? 0 : 1; 705 } 706 707 int compile_aidl_to_java(const JavaOptions& options, 708 const IoDelegate& io_delegate) { 709 unique_ptr<AidlInterface> interface; 710 std::vector<std::unique_ptr<AidlImport>> imports; 711 unique_ptr<java::JavaTypeNamespace> types(new java::JavaTypeNamespace()); 712 types->Init(); 713 AidlError aidl_err = internals::load_and_validate_aidl( 714 options.preprocessed_files_, 715 options.import_paths_, 716 options.input_file_name_, 717 options.gen_traces_, 718 io_delegate, 719 types.get(), 720 &interface, 721 &imports); 722 if (aidl_err == AidlError::FOUND_PARCELABLE && !options.fail_on_parcelable_) { 723 // We aborted code generation because this file contains parcelables. 724 // However, we were not told to complain if we find parcelables. 725 // Just generate a dep file and exit quietly. The dep file is for a legacy 726 // use case by the SDK. 727 write_java_dep_file(options, imports, io_delegate, ""); 728 return 0; 729 } 730 if (aidl_err != AidlError::OK) { 731 return 1; 732 } 733 734 string output_file_name = options.output_file_name_; 735 // if needed, generate the output file name from the base folder 736 if (output_file_name.empty() && !options.output_base_folder_.empty()) { 737 output_file_name = generate_outputFileName(options, *interface); 738 } 739 740 // make sure the folders of the output file all exists 741 if (!io_delegate.CreatePathForFile(output_file_name)) { 742 return 1; 743 } 744 745 if (!write_java_dep_file(options, imports, io_delegate, output_file_name)) { 746 return 1; 747 } 748 749 return generate_java(output_file_name, options.input_file_name_.c_str(), 750 interface.get(), types.get(), io_delegate, options); 751 } 752 753 bool preprocess_aidl(const JavaOptions& options, 754 const IoDelegate& io_delegate) { 755 unique_ptr<CodeWriter> writer = 756 io_delegate.GetCodeWriter(options.output_file_name_); 757 758 for (const auto& file : options.files_to_preprocess_) { 759 Parser p{io_delegate}; 760 if (!p.ParseFile(file)) 761 return false; 762 AidlDocument* doc = p.GetDocument(); 763 string line; 764 765 const AidlInterface* interface = doc->GetInterface(); 766 767 if (interface != nullptr && 768 !writer->Write("interface %s;\n", 769 interface->GetCanonicalName().c_str())) { 770 return false; 771 } 772 773 for (const auto& parcelable : doc->GetParcelables()) { 774 if (!writer->Write("parcelable %s;\n", 775 parcelable->GetCanonicalName().c_str())) { 776 return false; 777 } 778 } 779 } 780 781 return writer->Close(); 782 } 783 784 } // namespace android 785 } // namespace aidl 786