• 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 std::unique_ptr<android::ApkAssets> & assets,CompilationTarget target,std::ostream & target_out)83 void CompileApkAssetsLayouts(const std::unique_ptr<android::ApkAssets>& assets,
84                              CompilationTarget target, std::ostream& target_out) {
85   android::AssetManager2 resources;
86   resources.SetApkAssets({assets.get()});
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/", [&](const android::StringPiece& s,
104                                                        android::FileType) {
105     if (s == "layout") {
106       auto path = StringPrintf("res/%s/", s.to_string().c_str());
107       assets->GetAssetsProvider()->ForEachFile(path, [&](const android::StringPiece& layout_file,
108                                                          android::FileType) {
109         auto layout_path = StringPrintf("%s%s", path.c_str(), layout_file.to_string().c_str());
110         android::ApkAssetsCookie cookie = android::kInvalidCookie;
111         auto asset = resources.OpenNonAsset(layout_path, android::Asset::ACCESS_RANDOM, &cookie);
112         CHECK(asset);
113         CHECK(android::kInvalidCookie != cookie);
114         const auto dynamic_ref_table = resources.GetDynamicRefTableForCookie(cookie);
115         CHECK(nullptr != dynamic_ref_table);
116         android::ResXMLTree xml_tree{dynamic_ref_table};
117         xml_tree.setTo(asset->getBuffer(/*wordAligned=*/true),
118                        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 = startop::util::FindLayoutNameFromFilename(layout_path);
125           ResXmlVisitorAdapter adapter{&parser};
126           switch (target) {
127             case CompilationTarget::kDex: {
128               methods.push_back(compiled_view.CreateMethod(
129                   layout_name,
130                   dex::Prototype{dex::TypeDescriptor::FromClassname("android.view.View"),
131                                  dex::TypeDescriptor::FromClassname("android.content.Context"),
132                                  dex::TypeDescriptor::Int()}));
133               DexViewBuilder builder(&methods.back());
134               builder.Start();
135               LayoutCompilerVisitor visitor{&builder};
136               adapter.Accept(&visitor);
137               builder.Finish();
138               methods.back().Encode();
139               break;
140             }
141             case CompilationTarget::kJavaLanguage: {
142               JavaLangViewBuilder builder{package_name, layout_name, target_out};
143               builder.Start();
144               LayoutCompilerVisitor visitor{&builder};
145               adapter.Accept(&visitor);
146               builder.Finish();
147               break;
148             }
149           }
150         }
151       });
152     }
153   });
154 
155   if (target == CompilationTarget::kDex) {
156     slicer::MemView image{dex_file.CreateImage()};
157     target_out.write(image.ptr<const char>(), image.size());
158   }
159 }
160 }  // namespace
161 
CompileApkLayouts(const std::string & filename,CompilationTarget target,std::ostream & target_out)162 void CompileApkLayouts(const std::string& filename, CompilationTarget target,
163                        std::ostream& target_out) {
164   auto assets = android::ApkAssets::Load(filename);
165   CompileApkAssetsLayouts(assets, target, target_out);
166 }
167 
CompileApkLayoutsFd(android::base::unique_fd fd,CompilationTarget target,std::ostream & target_out)168 void CompileApkLayoutsFd(android::base::unique_fd fd, CompilationTarget target,
169                          std::ostream& target_out) {
170   constexpr const char* friendly_name{"viewcompiler assets"};
171   auto assets = android::ApkAssets::LoadFromFd(std::move(fd), friendly_name);
172   CompileApkAssetsLayouts(assets, target, target_out);
173 }
174 
175 }  // namespace startop
176