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 IoDelegate& io_delegate, 552 TypeNamespace* types, 553 std::unique_ptr<AidlInterface>* returned_interface, 554 std::vector<std::unique_ptr<AidlImport>>* returned_imports) { 555 AidlError err = AidlError::OK; 556 557 std::map<AidlImport*,std::unique_ptr<AidlDocument>> docs; 558 559 // import the preprocessed file 560 for (const string& s : preprocessed_files) { 561 if (!parse_preprocessed_file(io_delegate, s, types)) { 562 err = AidlError::BAD_PRE_PROCESSED_FILE; 563 } 564 } 565 if (err != AidlError::OK) { 566 return err; 567 } 568 569 // parse the input file 570 Parser p{io_delegate}; 571 if (!p.ParseFile(input_file_name)) { 572 return AidlError::PARSE_ERROR; 573 } 574 575 AidlDocument* parsed_doc = p.GetDocument(); 576 577 unique_ptr<AidlInterface> interface(parsed_doc->ReleaseInterface()); 578 579 if (!interface) { 580 LOG(ERROR) << "refusing to generate code from aidl file defining " 581 "parcelable"; 582 return AidlError::FOUND_PARCELABLE; 583 } 584 585 if (!check_filename(input_file_name.c_str(), interface->GetPackage(), 586 interface->GetName(), interface->GetLine()) || 587 !types->IsValidPackage(interface->GetPackage())) { 588 LOG(ERROR) << "Invalid package declaration '" << interface->GetPackage() 589 << "'"; 590 return AidlError::BAD_PACKAGE; 591 } 592 593 // parse the imports of the input file 594 ImportResolver import_resolver{io_delegate, import_paths}; 595 for (auto& import : p.GetImports()) { 596 if (types->HasImportType(*import)) { 597 // There are places in the Android tree where an import doesn't resolve, 598 // but we'll pick the type up through the preprocessed types. 599 // This seems like an error, but legacy support demands we support it... 600 continue; 601 } 602 string import_path = import_resolver.FindImportFile(import->GetNeededClass()); 603 if (import_path.empty()) { 604 cerr << import->GetFileFrom() << ":" << import->GetLine() 605 << ": couldn't find import for class " 606 << import->GetNeededClass() << endl; 607 err = AidlError::BAD_IMPORT; 608 continue; 609 } 610 import->SetFilename(import_path); 611 612 Parser p{io_delegate}; 613 if (!p.ParseFile(import->GetFilename())) { 614 cerr << "error while parsing import for class " 615 << import->GetNeededClass() << endl; 616 err = AidlError::BAD_IMPORT; 617 continue; 618 } 619 620 std::unique_ptr<AidlDocument> document(p.ReleaseDocument()); 621 if (!check_filenames(import->GetFilename(), document.get())) 622 err = AidlError::BAD_IMPORT; 623 docs[import.get()] = std::move(document); 624 } 625 if (err != AidlError::OK) { 626 return err; 627 } 628 629 // gather the types that have been declared 630 if (!types->AddBinderType(*interface.get(), input_file_name)) { 631 err = AidlError::BAD_TYPE; 632 } 633 634 interface->SetLanguageType(types->GetInterfaceType(*interface)); 635 636 for (const auto& import : p.GetImports()) { 637 // If we skipped an unresolved import above (see comment there) we'll have 638 // an empty bucket here. 639 const auto import_itr = docs.find(import.get()); 640 if (import_itr == docs.cend()) { 641 continue; 642 } 643 644 if (!gather_types(import->GetFilename(), import_itr->second.get(), types)) { 645 err = AidlError::BAD_TYPE; 646 } 647 } 648 649 // check the referenced types in parsed_doc to make sure we've imported them 650 if (check_types(input_file_name, interface.get(), types) != 0) { 651 err = AidlError::BAD_TYPE; 652 } 653 if (err != AidlError::OK) { 654 return err; 655 } 656 657 658 // assign method ids and validate. 659 if (check_and_assign_method_ids(input_file_name.c_str(), 660 interface->GetMethods()) != 0) { 661 return AidlError::BAD_METHOD_ID; 662 } 663 if (!validate_constants(*interface)) { 664 return AidlError::BAD_CONSTANTS; 665 } 666 667 if (returned_interface) 668 *returned_interface = std::move(interface); 669 670 if (returned_imports) 671 p.ReleaseImports(returned_imports); 672 673 return AidlError::OK; 674 } 675 676 } // namespace internals 677 678 int compile_aidl_to_cpp(const CppOptions& options, 679 const IoDelegate& io_delegate) { 680 unique_ptr<AidlInterface> interface; 681 std::vector<std::unique_ptr<AidlImport>> imports; 682 unique_ptr<cpp::TypeNamespace> types(new cpp::TypeNamespace()); 683 types->Init(); 684 AidlError err = internals::load_and_validate_aidl( 685 std::vector<std::string>{}, // no preprocessed files 686 options.ImportPaths(), 687 options.InputFileName(), 688 io_delegate, 689 types.get(), 690 &interface, 691 &imports); 692 if (err != AidlError::OK) { 693 return 1; 694 } 695 696 if (!write_cpp_dep_file(options, *interface, imports, io_delegate)) { 697 return 1; 698 } 699 700 return (cpp::GenerateCpp(options, *types, *interface, io_delegate)) ? 0 : 1; 701 } 702 703 int compile_aidl_to_java(const JavaOptions& options, 704 const IoDelegate& io_delegate) { 705 unique_ptr<AidlInterface> interface; 706 std::vector<std::unique_ptr<AidlImport>> imports; 707 unique_ptr<java::JavaTypeNamespace> types(new java::JavaTypeNamespace()); 708 types->Init(); 709 AidlError aidl_err = internals::load_and_validate_aidl( 710 options.preprocessed_files_, 711 options.import_paths_, 712 options.input_file_name_, 713 io_delegate, 714 types.get(), 715 &interface, 716 &imports); 717 if (aidl_err == AidlError::FOUND_PARCELABLE && !options.fail_on_parcelable_) { 718 // We aborted code generation because this file contains parcelables. 719 // However, we were not told to complain if we find parcelables. 720 // Just generate a dep file and exit quietly. The dep file is for a legacy 721 // use case by the SDK. 722 write_java_dep_file(options, imports, io_delegate, ""); 723 return 0; 724 } 725 if (aidl_err != AidlError::OK) { 726 return 1; 727 } 728 729 string output_file_name = options.output_file_name_; 730 // if needed, generate the output file name from the base folder 731 if (output_file_name.empty() && !options.output_base_folder_.empty()) { 732 output_file_name = generate_outputFileName(options, *interface); 733 } 734 735 // make sure the folders of the output file all exists 736 if (!io_delegate.CreatePathForFile(output_file_name)) { 737 return 1; 738 } 739 740 if (!write_java_dep_file(options, imports, io_delegate, output_file_name)) { 741 return 1; 742 } 743 744 return generate_java(output_file_name, options.input_file_name_.c_str(), 745 interface.get(), types.get(), io_delegate); 746 } 747 748 bool preprocess_aidl(const JavaOptions& options, 749 const IoDelegate& io_delegate) { 750 unique_ptr<CodeWriter> writer = 751 io_delegate.GetCodeWriter(options.output_file_name_); 752 753 for (const auto& file : options.files_to_preprocess_) { 754 Parser p{io_delegate}; 755 if (!p.ParseFile(file)) 756 return false; 757 AidlDocument* doc = p.GetDocument(); 758 string line; 759 760 const AidlInterface* interface = doc->GetInterface(); 761 762 if (interface != nullptr && 763 !writer->Write("interface %s;\n", 764 interface->GetCanonicalName().c_str())) { 765 return false; 766 } 767 768 for (const auto& parcelable : doc->GetParcelables()) { 769 if (!writer->Write("parcelable %s;\n", 770 parcelable->GetCanonicalName().c_str())) { 771 return false; 772 } 773 } 774 } 775 776 return writer->Close(); 777 } 778 779 } // namespace android 780 } // namespace aidl 781