• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2018 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 "apk_layout_compiler.h"
18 #include "dex_layout_compiler.h"
19 #include "java_lang_builder.h"
20 #include "layout_validation.h"
21 #include "util.h"
22 
23 #include "androidfw/ApkAssets.h"
24 #include "androidfw/AssetManager2.h"
25 #include "androidfw/ResourceTypes.h"
26 
27 #include <iostream>
28 #include <locale>
29 
30 #include "android-base/stringprintf.h"
31 
32 namespace startop {
33 
34 using android::ResXMLParser;
35 using android::base::StringPrintf;
36 
37 class ResXmlVisitorAdapter {
38  public:
ResXmlVisitorAdapter(ResXMLParser * parser)39   ResXmlVisitorAdapter(ResXMLParser* parser) : parser_{parser} {}
40 
41   template <typename Visitor>
Accept(Visitor * visitor)42   void Accept(Visitor* visitor) {
43     size_t depth{0};
44     do {
45       switch (parser_->next()) {
46         case ResXMLParser::START_DOCUMENT:
47           depth++;
48           visitor->VisitStartDocument();
49           break;
50         case ResXMLParser::END_DOCUMENT:
51           depth--;
52           visitor->VisitEndDocument();
53           break;
54         case ResXMLParser::START_TAG: {
55           depth++;
56           size_t name_length = 0;
57           const char16_t* name = parser_->getElementName(&name_length);
58           visitor->VisitStartTag(std::u16string{name, name_length});
59           break;
60         }
61         case ResXMLParser::END_TAG:
62           depth--;
63           visitor->VisitEndTag();
64           break;
65         default:;
66       }
67     } while (depth > 0 || parser_->getEventType() == ResXMLParser::FIRST_CHUNK_CODE);
68   }
69 
70  private:
71   ResXMLParser* parser_;
72 };
73 
CanCompileLayout(ResXMLParser * parser)74 bool CanCompileLayout(ResXMLParser* parser) {
75   ResXmlVisitorAdapter adapter{parser};
76   LayoutValidationVisitor visitor;
77   adapter.Accept(&visitor);
78 
79   return visitor.can_compile();
80 }
81 
82 namespace {
CompileApkAssetsLayouts(const android::ApkAssetsPtr & assets,CompilationTarget target,std::ostream & target_out)83 void CompileApkAssetsLayouts(const android::ApkAssetsPtr& assets, CompilationTarget target,
84                              std::ostream& target_out) {
85   android::AssetManager2 resources;
86   resources.SetApkAssets({assets});
87 
88   std::string package_name;
89 
90   // TODO: handle multiple packages better
91   bool first = true;
92   for (const auto& package : assets->GetLoadedArsc()->GetPackages()) {
93     CHECK(first);
94     package_name = package->GetPackageName();
95     first = false;
96   }
97 
98   dex::DexBuilder dex_file;
99   dex::ClassBuilder compiled_view{
100       dex_file.MakeClass(StringPrintf("%s.CompiledView", package_name.c_str()))};
101   std::vector<dex::MethodBuilder> methods;
102 
103   assets->GetAssetsProvider()->ForEachFile("res/", [&](android::StringPiece s, android::FileType) {
104       if (s == "layout") {
105           auto path = StringPrintf("res/%.*s/", (int)s.size(), s.data());
106           assets->GetAssetsProvider()
107                   ->ForEachFile(path, [&](android::StringPiece layout_file, android::FileType) {
108                       auto layout_path = StringPrintf("%s%.*s", path.c_str(),
109                                                       (int)layout_file.size(), layout_file.data());
110                       android::ApkAssetsCookie cookie = android::kInvalidCookie;
111                       auto asset = resources.OpenNonAsset(layout_path,
112                                                           android::Asset::ACCESS_RANDOM, &cookie);
113                       CHECK(asset);
114                       CHECK(android::kInvalidCookie != cookie);
115                       const auto dynamic_ref_table = resources.GetDynamicRefTableForCookie(cookie);
116                       CHECK(nullptr != dynamic_ref_table);
117                       android::ResXMLTree xml_tree{dynamic_ref_table};
118                       xml_tree.setTo(asset->getBuffer(/*wordAligned=*/true), asset->getLength(),
119                                      /*copy_data=*/true);
120                       android::ResXMLParser parser{xml_tree};
121                       parser.restart();
122                       if (CanCompileLayout(&parser)) {
123                           parser.restart();
124                           const std::string layout_name =
125                                   startop::util::FindLayoutNameFromFilename(layout_path);
126                           ResXmlVisitorAdapter adapter{&parser};
127                           switch (target) {
128                               case CompilationTarget::kDex: {
129                                   methods.push_back(compiled_view.CreateMethod(
130                                           layout_name,
131                                           dex::Prototype{dex::TypeDescriptor::FromClassname(
132                                                                  "android.view.View"),
133                                                          dex::TypeDescriptor::FromClassname(
134                                                                  "android.content.Context"),
135                                                          dex::TypeDescriptor::Int()}));
136                                   DexViewBuilder builder(&methods.back());
137                                   builder.Start();
138                                   LayoutCompilerVisitor visitor{&builder};
139                                   adapter.Accept(&visitor);
140                                   builder.Finish();
141                                   methods.back().Encode();
142                                   break;
143                               }
144                               case CompilationTarget::kJavaLanguage: {
145                                   JavaLangViewBuilder builder{package_name, layout_name,
146                                                               target_out};
147                                   builder.Start();
148                                   LayoutCompilerVisitor visitor{&builder};
149                                   adapter.Accept(&visitor);
150                                   builder.Finish();
151                                   break;
152                               }
153                           }
154                       }
155                   });
156       }
157   });
158 
159   if (target == CompilationTarget::kDex) {
160     slicer::MemView image{dex_file.CreateImage()};
161     target_out.write(image.ptr<const char>(), image.size());
162   }
163 }
164 }  // namespace
165 
CompileApkLayouts(const std::string & filename,CompilationTarget target,std::ostream & target_out)166 void CompileApkLayouts(const std::string& filename, CompilationTarget target,
167                        std::ostream& target_out) {
168   auto assets = android::ApkAssets::Load(filename);
169   CompileApkAssetsLayouts(assets, target, target_out);
170 }
171 
CompileApkLayoutsFd(android::base::unique_fd fd,CompilationTarget target,std::ostream & target_out)172 void CompileApkLayoutsFd(android::base::unique_fd fd, CompilationTarget target,
173                          std::ostream& target_out) {
174   constexpr const char* friendly_name{"viewcompiler assets"};
175   auto assets = android::ApkAssets::LoadFromFd(std::move(fd), friendly_name);
176   CompileApkAssetsLayouts(assets, target, target_out);
177 }
178 
179 }  // namespace startop
180