blob: 4d80b06d0d5a1b155599368bb2ba549ba3c5898f [file] [log] [blame]
Andreas Huber881227d2016-08-02 14:20:21 -07001#include "AST.h"
2
3#include "Coordinator.h"
4#include "Formatter.h"
5#include "Interface.h"
6#include "Method.h"
7#include "Scope.h"
8
9#include <android-base/logging.h>
10#include <sys/stat.h>
11#include <vector>
12
13namespace android {
14
15static bool MakeParentHierarchy(const std::string &path) {
16 static const mode_t kMode = 0755;
17
18 size_t start = 1; // Ignore leading '/'
19 size_t slashPos;
20 while ((slashPos = path.find("/", start)) != std::string::npos) {
21 std::string partial = path.substr(0, slashPos);
22
23 struct stat st;
24 if (stat(partial.c_str(), &st) < 0) {
25 if (errno != ENOENT) {
26 return false;
27 }
28
29 int res = mkdir(partial.c_str(), kMode);
30 if (res < 0) {
31 return false;
32 }
33 } else if (!S_ISDIR(st.st_mode)) {
34 return false;
35 }
36
37 start = slashPos + 1;
38 }
39
40 return true;
41}
42
43static void SplitString(
44 const std::string &s, char c, std::vector<std::string> *components) {
45 components->clear();
46
47 size_t startPos = 0;
48 size_t matchPos;
49 while ((matchPos = s.find(c, startPos)) != std::string::npos) {
50 components->push_back(s.substr(startPos, matchPos - startPos));
51 startPos = matchPos + 1;
52 }
53
54 if (startPos + 1 < s.length()) {
55 components->push_back(s.substr(startPos));
56 }
57}
58
59static std::string upcase(const std::string in) {
60 std::string out{in};
61
62 for (auto &ch : out) {
63 ch = toupper(ch);
64 }
65
66 return out;
67}
68
Andreas Huberb82318c2016-08-02 14:45:54 -070069status_t AST::generateCpp(const std::string &outputPath) const {
70 status_t err = generateInterfaceHeader(outputPath);
Andreas Huber881227d2016-08-02 14:20:21 -070071
72 if (err == OK) {
Andreas Huberb82318c2016-08-02 14:45:54 -070073 err = generateStubHeader(outputPath);
Andreas Huber881227d2016-08-02 14:20:21 -070074 }
75
76 if (err == OK) {
Andreas Huberb82318c2016-08-02 14:45:54 -070077 err = generateProxyHeader(outputPath);
Andreas Huber881227d2016-08-02 14:20:21 -070078 }
79
80 if (err == OK) {
Andreas Huberb82318c2016-08-02 14:45:54 -070081 err = generateAllSource(outputPath);
Andreas Huber881227d2016-08-02 14:20:21 -070082 }
83
84 return err;
85}
86
87void AST::getPackageComponents(
88 std::vector<std::string> *components) const {
89 SplitString(mPackage.package(), '.', components);
90}
91
92void AST::getPackageAndVersionComponents(
93 std::vector<std::string> *components, bool cpp_compatible) const {
94 getPackageComponents(components);
95
96 const std::string packageVersion = mPackage.version();
97 CHECK(packageVersion[0] == '@');
98
99 if (!cpp_compatible) {
100 components->push_back(packageVersion.substr(1));
101 return;
102 }
103
104 const size_t dotPos = packageVersion.find('.');
105
106 // Form "Vmajor_minor".
107 std::string versionString = "V";
108 versionString.append(packageVersion.substr(1, dotPos - 1));
109 versionString.append("_");
110 versionString.append(packageVersion.substr(dotPos + 1));
111
112 components->push_back(versionString);
113}
114
115std::string AST::makeHeaderGuard(const std::string &baseName) const {
116 std::vector<std::string> packageComponents;
117 getPackageAndVersionComponents(
118 &packageComponents, true /* cpp_compatible */);
119
120 std::string guard = "HIDL_GENERATED";
121 for (const auto &component : packageComponents) {
122 guard += "_";
123 guard += component;
124 }
125
126 guard += "_";
127 guard += baseName;
128 guard += "_H_";
129
130 return guard;
131}
132
133void AST::enterLeaveNamespace(Formatter &out, bool enter) const {
134 std::vector<std::string> packageComponents;
135 getPackageAndVersionComponents(
136 &packageComponents, true /* cpp_compatible */);
137
138 if (enter) {
139 for (const auto &component : packageComponents) {
140 out << "namespace " << component << " {\n";
141 }
142 } else {
143 for (auto it = packageComponents.rbegin();
144 it != packageComponents.rend();
145 ++it) {
146 out << "} // namespace " << *it << "\n";
147 }
148 }
149}
150
Andreas Huberb82318c2016-08-02 14:45:54 -0700151status_t AST::generateInterfaceHeader(const std::string &outputPath) const {
Andreas Huber881227d2016-08-02 14:20:21 -0700152 const std::string packagePath =
153 mCoordinator->getPackagePath(mPackage, true /* relative */);
154
Andreas Huberb82318c2016-08-02 14:45:54 -0700155 std::string path = outputPath;
156 path.append("android/hardware/");
Andreas Huber881227d2016-08-02 14:20:21 -0700157 path.append(packagePath);
158
159 std::string ifaceName;
160 bool isInterface = true;
161 if (!AST::isInterface(&ifaceName)) {
162 ifaceName = "types";
163 isInterface = false;
164 }
165 path.append(ifaceName);
166 path.append(".h");
167
168 CHECK(MakeParentHierarchy(path));
169 FILE *file = fopen(path.c_str(), "w");
170
171 if (file == NULL) {
172 return -errno;
173 }
174
175 Formatter out(file);
176
177 const std::string guard = makeHeaderGuard(ifaceName);
178
179 out << "#ifndef " << guard << "\n";
180 out << "#define " << guard << "\n\n";
181
182 out << "#include <hwbinder/HidlSupport.h>\n";
183
184 if (isInterface) {
185 out << "#include <hwbinder/IBinder.h>\n";
186 out << "#include <hwbinder/IInterface.h>\n";
187 out << "#include <hwbinder/Status.h>\n";
188 }
189
190 out << "#include <utils/NativeHandle.h>\n\n";
191
192 enterLeaveNamespace(out, true /* enter */);
193 out << "\n";
194
195 if (isInterface) {
196 out << "struct "
197 << ifaceName
198 << " : public ::android::hidl::IInterface {\n";
199
200 out.indent();
201
202 out << "DECLARE_HWBINDER_META_INTERFACE(" << ifaceName << ");\n\n";
203 }
204
205 status_t err = emitTypeDeclarations(out);
206
207 if (err != OK) {
208 return err;
209 }
210
211 if (isInterface) {
212 const Interface *iface = mRootScope->getInterface();
213
214 out << "enum Call {\n";
215 out.indent();
216
217 bool first = true;
218 for (const auto &method : iface->methods()) {
219 out << upcase(method->name());
220
221 if (first) {
222 out << " = ::android::hidl::IBinder::FIRST_CALL_TRANSACTION";
223 first = false;
224 }
225
226 out << ",\n";
227 }
228
229 out.unindent();
230 out << "};\n\n";
231
232 bool haveCallbacks = false;
233 for (const auto &method : iface->methods()) {
234 const bool returnsValue = !method->results().empty();
235
236 if (!returnsValue) {
237 continue;
238 }
239
240 haveCallbacks = true;
241
242 out << "using "
243 << method->name()
244 << "_cb = std::function<void("
245 << Method::GetSignature(method->results())
246 << ")>;\n";
247 }
248
249 if (haveCallbacks) {
250 out << "\n";
251 }
252
253 for (const auto &method : iface->methods()) {
254 const bool returnsValue = !method->results().empty();
255
256 out << "virtual ::android::hidl::binder::Status "
257 << method->name()
258 << "("
259 << Method::GetSignature(method->args());
260
261 if (returnsValue) {
262 if (!method->args().empty()) {
263 out << ", ";
264 }
265
266 out << method->name() << "_cb _aidl_cb = nullptr";
267 }
268
269 out << ") = 0;\n";
270 }
271 }
272
273 if (isInterface) {
274 out.unindent();
275
276 out << "};\n";
277 }
278
279 out << "\n";
280 enterLeaveNamespace(out, false /* enter */);
281
282 out << "\n#endif // " << guard << "\n";
283
284 return OK;
285}
286
287status_t AST::emitTypeDeclarations(Formatter &out) const {
288 return mRootScope->emitTypeDeclarations(out);
289}
290
Andreas Huberb82318c2016-08-02 14:45:54 -0700291status_t AST::generateStubHeader(const std::string &outputPath) const {
Andreas Huber881227d2016-08-02 14:20:21 -0700292 std::string ifaceName;
293 if (!AST::isInterface(&ifaceName)) {
294 // types.hal does not get a stub header.
295 return OK;
296 }
297
298 const std::string packagePath =
299 mCoordinator->getPackagePath(mPackage, true /* relative */);
300
301 // cut off the leading 'I'.
302 const std::string baseName = ifaceName.substr(1);
303
Andreas Huberb82318c2016-08-02 14:45:54 -0700304 std::string path = outputPath;
305 path.append("android/hardware/");
Andreas Huber881227d2016-08-02 14:20:21 -0700306 path.append(packagePath);
307 path.append("Bn");
308 path.append(baseName);
309 path.append(".h");
310
311 CHECK(MakeParentHierarchy(path));
312 FILE *file = fopen(path.c_str(), "w");
313
314 if (file == NULL) {
315 return -errno;
316 }
317
318 Formatter out(file);
319
320 const std::string guard = makeHeaderGuard("Bn" + baseName);
321
322 out << "#ifndef " << guard << "\n";
323 out << "#define " << guard << "\n\n";
324
325 std::vector<std::string> packageComponents;
326 getPackageAndVersionComponents(
327 &packageComponents, false /* cpp_compatible */);
328
329 out << "#include <";
330 for (const auto &component : packageComponents) {
331 out << component << "/";
332 }
333 out << ifaceName << ".h>\n\n";
334
335 enterLeaveNamespace(out, true /* enter */);
336 out << "\n";
337
338 out << "struct "
339 << "Bn"
340 << baseName
341 << " : public ::android::hidl::BnInterface<"
342 << ifaceName
343 << "> {\n";
344
345 out.indent();
346
347 out << "::android::status_t onTransact(\n";
348 out.indent();
349 out.indent();
350 out << "uint32_t _aidl_code,\n";
351 out << "const ::android::hidl::Parcel &_aidl_data,\n";
352 out << "::android::hidl::Parcel *_aidl_reply,\n";
353 out << "uint32_t _aidl_flags = 0,\n";
354 out << "TransactCallback _aidl_cb = nullptr) override;\n";
355 out.unindent();
356 out.unindent();
357
358 out.unindent();
359
360 out << "};\n\n";
361
362 enterLeaveNamespace(out, false /* enter */);
363
364 out << "\n#endif // " << guard << "\n";
365
366 return OK;
367}
368
Andreas Huberb82318c2016-08-02 14:45:54 -0700369status_t AST::generateProxyHeader(const std::string &outputPath) const {
Andreas Huber881227d2016-08-02 14:20:21 -0700370 std::string ifaceName;
371 if (!AST::isInterface(&ifaceName)) {
372 // types.hal does not get a proxy header.
373 return OK;
374 }
375
376 const std::string packagePath =
377 mCoordinator->getPackagePath(mPackage, true /* relative */);
378
379 // cut off the leading 'I'.
380 const std::string baseName = ifaceName.substr(1);
381
Andreas Huberb82318c2016-08-02 14:45:54 -0700382 std::string path = outputPath;
383 path.append("android/hardware/");
Andreas Huber881227d2016-08-02 14:20:21 -0700384 path.append(packagePath);
385 path.append("Bp");
386 path.append(baseName);
387 path.append(".h");
388
389 CHECK(MakeParentHierarchy(path));
390 FILE *file = fopen(path.c_str(), "w");
391
392 if (file == NULL) {
393 return -errno;
394 }
395
396 Formatter out(file);
397
398 const std::string guard = makeHeaderGuard("Bp" + baseName);
399
400 out << "#ifndef " << guard << "\n";
401 out << "#define " << guard << "\n\n";
402
403 std::vector<std::string> packageComponents;
404 getPackageAndVersionComponents(
405 &packageComponents, false /* cpp_compatible */);
406
407 out << "#include <";
408 for (const auto &component : packageComponents) {
409 out << component << "/";
410 }
411 out << ifaceName << ".h>\n\n";
412
413 enterLeaveNamespace(out, true /* enter */);
414 out << "\n";
415
416 out << "struct "
417 << "Bp"
418 << baseName
419 << " : public ::android::hidl::BpInterface<"
420 << ifaceName
421 << "> {\n";
422
423 out.indent();
424
425 out << "explicit Bp"
426 << baseName
427 << "(const ::android::sp<::android::hidl::IBinder> &_aidl_impl);"
428 << "\n\n";
429
430 const Interface *iface = mRootScope->getInterface();
431
432 for (const auto &method : iface->methods()) {
433 const bool returnsValue = !method->results().empty();
434
435 out << "::android::hidl::binder::Status "
436 << method->name()
437 << "("
438 << Method::GetSignature(method->args());
439
440 if (returnsValue) {
441 if (!method->args().empty()) {
442 out << ", ";
443 }
444
445 out << method->name() << "_cb _aidl_cb";
446 }
447
448 out << ") override;\n";
449 }
450
451 out.unindent();
452
453 out << "};\n\n";
454
455 enterLeaveNamespace(out, false /* enter */);
456
457 out << "\n#endif // " << guard << "\n";
458
459 return OK;
460}
461
Andreas Huberb82318c2016-08-02 14:45:54 -0700462status_t AST::generateAllSource(const std::string &outputPath) const {
Andreas Huber881227d2016-08-02 14:20:21 -0700463 const std::string packagePath =
464 mCoordinator->getPackagePath(mPackage, true /* relative */);
465
Andreas Huberb82318c2016-08-02 14:45:54 -0700466 std::string path = outputPath;
467 path.append("android/hardware/");
Andreas Huber881227d2016-08-02 14:20:21 -0700468 path.append(packagePath);
469
470 std::string ifaceName;
471 std::string baseName;
472
473 bool isInterface = true;
474 if (!AST::isInterface(&ifaceName)) {
475 baseName = "types";
476 isInterface = false;
477 } else {
478 baseName = ifaceName.substr(1); // cut off the leading 'I'.
479 }
480
481 path.append(baseName);
482
483 if (baseName != "types") {
484 path.append("All");
485 }
486
487 path.append(".cpp");
488
489 CHECK(MakeParentHierarchy(path));
490 FILE *file = fopen(path.c_str(), "w");
491
492 if (file == NULL) {
493 return -errno;
494 }
495
496 Formatter out(file);
497
498 std::vector<std::string> packageComponents;
499 getPackageAndVersionComponents(
500 &packageComponents, false /* cpp_compatible */);
501
502 std::string prefix;
503 for (const auto &component : packageComponents) {
504 prefix += component;
505 prefix += "/";
506 }
507
508 if (isInterface) {
509 out << "#include <" << prefix << "/Bp" << baseName << ".h>\n";
510 out << "#include <" << prefix << "/Bn" << baseName << ".h>\n";
511 } else {
512 out << "#include <" << prefix << "types.h>\n";
513 }
514
515 out << "\n";
516
517 enterLeaveNamespace(out, true /* enter */);
518 out << "\n";
519
520 status_t err = generateTypeSource(out, ifaceName);
521
522 if (err == OK && isInterface) {
523 err = generateProxySource(out, baseName);
524 }
525
526 if (err == OK && isInterface) {
527 err = generateStubSource(out, baseName);
528 }
529
530 enterLeaveNamespace(out, false /* enter */);
531
532 return err;
533}
534
535status_t AST::generateTypeSource(
536 Formatter &out, const std::string &ifaceName) const {
537 return mRootScope->emitTypeDefinitions(out, ifaceName);
538}
539
540void AST::emitCppReaderWriter(
541 Formatter &out,
542 const std::string &parcelObj,
543 bool parcelObjIsPointer,
544 const TypedVar *arg,
545 bool isReader,
546 Type::ErrorMode mode) const {
547 const Type &type = arg->type();
548
549 if (isReader) {
550 std::string extra;
551 out << type.getCppResultType(&extra)
552 << " "
553 << arg->name()
554 << extra
555 << ";\n";
556 }
557
558 type.emitReaderWriter(
559 out,
560 arg->name(),
561 parcelObj,
562 parcelObjIsPointer,
563 isReader,
564 mode);
565}
566
567status_t AST::generateProxySource(
568 Formatter &out, const std::string &baseName) const {
569 const std::string klassName = "Bp" + baseName;
570
571 out << klassName
572 << "::"
573 << klassName
574 << "(const ::android::sp<::android::hidl::IBinder> &_aidl_impl)\n";
575
576 out.indent();
577 out.indent();
578
579 out << ": BpInterface"
580 << "<I"
581 << baseName
582 << ">(_aidl_impl) {\n";
583
584 out.unindent();
585 out.unindent();
586 out << "}\n\n";
587
588 const Interface *iface = mRootScope->getInterface();
589
590 for (const auto &method : iface->methods()) {
591 const bool returnsValue = !method->results().empty();
592
593 out << "::android::hidl::binder::Status "
594 << klassName
595 << "::"
596 << method->name()
597 << "("
598 << Method::GetSignature(method->args());
599
600 if (returnsValue) {
601 if (!method->args().empty()) {
602 out << ", ";
603 }
604
605 out << method->name() << "_cb _aidl_cb";
606 }
607
608 out << ") {\n";
609
610 out.indent();
611
612 out << "::android::hidl::Parcel _aidl_data;\n";
613 out << "::android::hidl::Parcel _aidl_reply;\n";
614 out << "::android::status_t _aidl_err;\n\n";
615 out << "::android::hidl::binder::Status _aidl_status;\n";
616
617 out << "_aidl_err = _aidl_data.writeInterfaceToken("
618 "getInterfaceDescriptor());\n";
619
620 out << "if (_aidl_err != ::android::OK) { goto _aidl_error; }\n\n";
621
622 for (const auto &arg : method->args()) {
623 emitCppReaderWriter(
624 out,
625 "_aidl_data",
626 false /* parcelObjIsPointer */,
627 arg,
628 false /* reader */,
629 Type::ErrorMode_Goto);
630 }
631
632 out << "_aidl_err = remote()->transact(I"
633 << baseName
634 << "::"
635 << upcase(method->name())
636 << ", _aidl_data, &_aidl_reply);\n";
637
638 out << "if (_aidl_err != ::android::OK) { goto _aidl_error; }\n\n";
639
640 out << "_aidl_err = _aidl_status.readFromParcel(_aidl_reply);\n";
641 out << "if (_aidl_err != ::android::OK) { goto _aidl_error; }\n\n";
642
643 out << "if (!_aidl_status.isOk()) { return _aidl_status; }\n\n";
644
645 for (const auto &arg : method->results()) {
646 emitCppReaderWriter(
647 out,
648 "_aidl_reply",
649 false /* parcelObjIsPointer */,
650 arg,
651 true /* reader */,
652 Type::ErrorMode_Goto);
653 }
654
655 if (returnsValue) {
656 out << "if (_aidl_cb != nullptr) {\n";
657 out.indent();
658 out << "_aidl_cb(";
659
660 bool first = true;
661 for (const auto &arg : method->results()) {
662 if (!first) {
663 out << ", ";
664 }
665
666 if (arg->type().resultNeedsDeref()) {
667 out << "*";
668 }
669 out << arg->name();
670
671 first = false;
672 }
673
674 out << ");\n";
675 out.unindent();
676 out << "}\n\n";
677 }
678
679 out.unindent();
680 out << "_aidl_error:\n";
681 out.indent();
682 out << "_aidl_status.setFromStatusT(_aidl_err);\n"
683 << "return _aidl_status;\n";
684
685 out.unindent();
686 out << "}\n\n";
687 }
688
689 return OK;
690}
691
692status_t AST::generateStubSource(
693 Formatter &out, const std::string &baseName) const {
694 out << "IMPLEMENT_HWBINDER_META_INTERFACE("
695 << baseName
696 << ", \""
697 << mPackage.string()
698 << "::I"
699 << baseName
700 << "\");\n\n";
701
702 const std::string klassName = "Bn" + baseName;
703
704 out << "::android::status_t " << klassName << "::onTransact(\n";
705
706 out.indent();
707 out.indent();
708
709 out << "uint32_t _aidl_code,\n"
710 << "const ::android::hidl::Parcel &_aidl_data,\n"
711 << "::android::hidl::Parcel *_aidl_reply,\n"
712 << "uint32_t _aidl_flags,\n"
713 << "TransactCallback _aidl_cb) {\n";
714
715 out.unindent();
716
717 out << "::android::status_t _aidl_err = ::android::OK;\n\n";
718
719 out << "switch (_aidl_code) {\n";
720 out.indent();
721
722 const Interface *iface = mRootScope->getInterface();
723
724 for (const auto &method : iface->methods()) {
725 out << "case Call::" << upcase(method->name()) << ":\n{\n";
726 out.indent();
727
728 status_t err = generateStubSourceForMethod(out, method);
729
730 if (err != OK) {
731 return err;
732 }
733
734 out.unindent();
735 out << "}\n\n";
736 }
737
738 out << "default:\n{\n";
739 out.indent();
740
741 out << "return ::android::hidl::BnInterface<I"
742 << baseName
743 << ">::onTransact(\n";
744
745 out.indent();
746 out.indent();
747
748 out << "_aidl_code, _aidl_data, _aidl_reply, "
749 << "_aidl_flags, _aidl_cb);\n";
750
751 out.unindent();
752 out.unindent();
753
754 out.unindent();
755 out << "}\n";
756
757 out.unindent();
758 out << "}\n\n";
759
760 out << "if (_aidl_err == ::android::UNEXPECTED_NULL) {\n";
761 out.indent();
762 out << "_aidl_err = ::android::hidl::binder::Status::fromExceptionCode(\n";
763 out.indent();
764 out.indent();
765 out << "::android::hidl::binder::Status::EX_NULL_POINTER)\n";
766 out.indent();
767 out.indent();
768 out << ".writeToParcel(_aidl_reply);\n";
769 out.unindent();
770 out.unindent();
771 out.unindent();
772 out.unindent();
773
774 out.unindent();
775 out << "}\n\n";
776
777 out << "return _aidl_err;\n";
778
779 out.unindent();
780 out << "}\n\n";
781
782 return OK;
783}
784
785status_t AST::generateStubSourceForMethod(
786 Formatter &out, const Method *method) const {
787 out << "if (!_aidl_data.checkInterface(this)) {\n";
788 out.indent();
789 out << "_aidl_err = ::android::BAD_TYPE;\n";
790 out << "break;\n";
791 out.unindent();
792 out << "}\n\n";
793
794 for (const auto &arg : method->args()) {
795 emitCppReaderWriter(
796 out,
797 "_aidl_data",
798 false /* parcelObjIsPointer */,
799 arg,
800 true /* reader */,
801 Type::ErrorMode_Break);
802 }
803
804 const bool returnsValue = !method->results().empty();
805
806 if (returnsValue) {
807 out << "bool _aidl_callbackCalled = false;\n\n";
808 }
809
810 out << "::android::hidl::binder::Status _aidl_status(\n";
811 out.indent();
812 out.indent();
813 out << method->name() << "(";
814
815 bool first = true;
816 for (const auto &arg : method->args()) {
817 if (!first) {
818 out << ", ";
819 }
820
821 if (arg->type().resultNeedsDeref()) {
822 out << "*";
823 }
824
825 out << arg->name();
826
827 first = false;
828 }
829
830 if (returnsValue) {
831 if (!first) {
832 out << ", ";
833 }
834
835 out << "[&](";
836
837 first = true;
838 for (const auto &arg : method->results()) {
839 if (!first) {
840 out << ", ";
841 }
842
843 out << "const auto &" << arg->name();
844
845 first = false;
846 }
847
848 out << ") {\n";
849 out.indent();
850 out << "_aidl_callbackCalled = true;\n\n";
851
852 out << "::android::hidl::binder::Status::ok()"
853 << ".writeToParcel(_aidl_reply);\n\n";
854
855 for (const auto &arg : method->results()) {
856 emitCppReaderWriter(
857 out,
858 "_aidl_reply",
859 true /* parcelObjIsPointer */,
860 arg,
861 false /* reader */,
862 Type::ErrorMode_Ignore);
863 }
864
865 out << "_aidl_cb(*_aidl_reply);\n";
866
867 out.unindent();
868 out << "}\n";
869 }
870
871 out.unindent();
872 out.unindent();
873 out << "));\n\n";
874
875 if (returnsValue) {
876 out << "if (!_aidl_callbackCalled) {\n";
877 out.indent();
878 }
879
880 out << "_aidl_err = _aidl_status.writeToParcel(_aidl_reply);\n";
881
882 if (returnsValue) {
883 out.unindent();
884 out << "}\n\n";
885 }
886
887 out << "break;\n";
888
889 return OK;
890}
891
892} // namespace android
893