blob: 4d49674efce3c8c1bf294f1683676009257f09f0 [file] [log] [blame]
Ryan Mitchell2ed8bfa2021-01-08 13:34:28 -08001/*
2 * Copyright (C) 2021 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 "idmap2/FabricatedOverlay.h"
18
19#include <androidfw/ResourceUtils.h>
20#include <google/protobuf/io/coded_stream.h>
21#include <google/protobuf/io/zero_copy_stream_impl_lite.h>
22#include <utils/ByteOrder.h>
23#include <zlib.h>
24
25#include <fstream>
Mårten Kongstad1195a6b2021-05-11 12:57:01 +000026#include <map>
27#include <memory>
28#include <string>
29#include <utility>
Ryan Mitchell2ed8bfa2021-01-08 13:34:28 -080030
31namespace android::idmap2 {
32
33namespace {
34bool Read32(std::istream& stream, uint32_t* out) {
35 uint32_t value;
36 if (stream.read(reinterpret_cast<char*>(&value), sizeof(uint32_t))) {
37 *out = dtohl(value);
38 return true;
39 }
40 return false;
41}
42
43void Write32(std::ostream& stream, uint32_t value) {
44 uint32_t x = htodl(value);
45 stream.write(reinterpret_cast<char*>(&x), sizeof(uint32_t));
46}
47} // namespace
48
49FabricatedOverlay::FabricatedOverlay(pb::FabricatedOverlay&& overlay,
50 std::optional<uint32_t> crc_from_disk)
51 : overlay_pb_(std::forward<pb::FabricatedOverlay>(overlay)), crc_from_disk_(crc_from_disk) {
52}
53
54FabricatedOverlay::Builder::Builder(const std::string& package_name, const std::string& name,
55 const std::string& target_package_name) {
56 package_name_ = package_name;
57 name_ = name;
58 target_package_name_ = target_package_name;
59}
60
61FabricatedOverlay::Builder& FabricatedOverlay::Builder::SetOverlayable(const std::string& name) {
62 target_overlayable_ = name;
63 return *this;
64}
65
66FabricatedOverlay::Builder& FabricatedOverlay::Builder::SetResourceValue(
67 const std::string& resource_name, uint8_t data_type, uint32_t data_value) {
Jeremy Meyer05466592022-06-02 21:31:15 +000068 entries_.emplace_back(Entry{resource_name, data_type, data_value, ""});
69 return *this;
70}
71
72FabricatedOverlay::Builder& FabricatedOverlay::Builder::SetResourceValue(
73 const std::string& resource_name, uint8_t data_type, const std::string& data_string_value) {
74 entries_.emplace_back(Entry{resource_name, data_type, 0, data_string_value});
Ryan Mitchell2ed8bfa2021-01-08 13:34:28 -080075 return *this;
76}
77
78Result<FabricatedOverlay> FabricatedOverlay::Builder::Build() {
79 std::map<std::string, std::map<std::string, std::map<std::string, TargetValue>>> entries;
80 for (const auto& res_entry : entries_) {
81 StringPiece package_substr;
82 StringPiece type_name;
83 StringPiece entry_name;
84 if (!android::ExtractResourceName(StringPiece(res_entry.resource_name), &package_substr,
85 &type_name, &entry_name)) {
86 return Error("failed to parse resource name '%s'", res_entry.resource_name.c_str());
87 }
88
89 std::string package_name =
90 package_substr.empty() ? target_package_name_ : package_substr.to_string();
91 if (type_name.empty()) {
92 return Error("resource name '%s' missing type name", res_entry.resource_name.c_str());
93 }
94
95 if (entry_name.empty()) {
96 return Error("resource name '%s' missing entry name", res_entry.resource_name.c_str());
97 }
98
99 auto package = entries.find(package_name);
100 if (package == entries.end()) {
101 package = entries
Mårten Kongstad1195a6b2021-05-11 12:57:01 +0000102 .insert(std::make_pair(
Ryan Mitchell2ed8bfa2021-01-08 13:34:28 -0800103 package_name, std::map<std::string, std::map<std::string, TargetValue>>()))
104 .first;
105 }
106
107 auto type = package->second.find(type_name.to_string());
108 if (type == package->second.end()) {
109 type =
110 package->second
Mårten Kongstad1195a6b2021-05-11 12:57:01 +0000111 .insert(std::make_pair(type_name.to_string(), std::map<std::string, TargetValue>()))
Ryan Mitchell2ed8bfa2021-01-08 13:34:28 -0800112 .first;
113 }
114
115 auto entry = type->second.find(entry_name.to_string());
116 if (entry == type->second.end()) {
Mårten Kongstad1195a6b2021-05-11 12:57:01 +0000117 entry = type->second.insert(std::make_pair(entry_name.to_string(), TargetValue())).first;
Ryan Mitchell2ed8bfa2021-01-08 13:34:28 -0800118 }
119
Jeremy Meyer05466592022-06-02 21:31:15 +0000120 entry->second = TargetValue{
121 res_entry.data_type, res_entry.data_value, res_entry.data_string_value};
Ryan Mitchell2ed8bfa2021-01-08 13:34:28 -0800122 }
123
124 pb::FabricatedOverlay overlay_pb;
125 overlay_pb.set_package_name(package_name_);
126 overlay_pb.set_name(name_);
127 overlay_pb.set_target_package_name(target_package_name_);
128 overlay_pb.set_target_overlayable(target_overlayable_);
129
130 for (const auto& package : entries) {
131 auto package_pb = overlay_pb.add_packages();
132 package_pb->set_name(package.first);
133
134 for (const auto& type : package.second) {
135 auto type_pb = package_pb->add_types();
136 type_pb->set_name(type.first);
137
138 for (const auto& entry : type.second) {
139 auto entry_pb = type_pb->add_entries();
140 entry_pb->set_name(entry.first);
141 pb::ResourceValue* value = entry_pb->mutable_res_value();
142 value->set_data_type(entry.second.data_type);
143 value->set_data_value(entry.second.data_value);
144 }
145 }
146 }
147
148 return FabricatedOverlay(std::move(overlay_pb));
149}
150
151Result<FabricatedOverlay> FabricatedOverlay::FromBinaryStream(std::istream& stream) {
152 uint32_t magic;
153 if (!Read32(stream, &magic)) {
154 return Error("Failed to read fabricated overlay magic.");
155 }
156
157 if (magic != kFabricatedOverlayMagic) {
158 return Error("Not a fabricated overlay file.");
159 }
160
161 uint32_t version;
162 if (!Read32(stream, &version)) {
163 return Error("Failed to read fabricated overlay version.");
164 }
165
166 if (version != 1) {
167 return Error("Invalid fabricated overlay version '%u'.", version);
168 }
169
170 uint32_t crc;
171 if (!Read32(stream, &crc)) {
172 return Error("Failed to read fabricated overlay version.");
173 }
174
175 pb::FabricatedOverlay overlay{};
176 if (!overlay.ParseFromIstream(&stream)) {
177 return Error("Failed read fabricated overlay proto.");
178 }
179
180 // If the proto version is the latest version, then the contents of the proto must be the same
181 // when the proto is re-serialized; otherwise, the crc must be calculated because migrating the
182 // proto to the latest version will likely change the contents of the fabricated overlay.
183 return FabricatedOverlay(std::move(overlay), version == kFabricatedOverlayCurrentVersion
184 ? std::optional<uint32_t>(crc)
185 : std::nullopt);
186}
187
188Result<FabricatedOverlay::SerializedData*> FabricatedOverlay::InitializeData() const {
189 if (!data_.has_value()) {
190 auto size = overlay_pb_.ByteSizeLong();
191 auto data = std::unique_ptr<uint8_t[]>(new uint8_t[size]);
192
193 // Ensure serialization is deterministic
194 google::protobuf::io::ArrayOutputStream array_stream(data.get(), size);
195 google::protobuf::io::CodedOutputStream output_stream(&array_stream);
196 output_stream.SetSerializationDeterministic(true);
197 overlay_pb_.SerializeWithCachedSizes(&output_stream);
198 if (output_stream.HadError() || size != output_stream.ByteCount()) {
199 return Error("Failed to serialize fabricated overlay.");
200 }
201
202 // Calculate the crc using the proto data and the version.
203 uint32_t crc = crc32(0L, Z_NULL, 0);
204 crc = crc32(crc, reinterpret_cast<const uint8_t*>(&kFabricatedOverlayCurrentVersion),
205 sizeof(uint32_t));
206 crc = crc32(crc, data.get(), size);
207 data_ = SerializedData{std::move(data), size, crc};
208 }
209 return &(*data_);
210}
211Result<uint32_t> FabricatedOverlay::GetCrc() const {
212 if (crc_from_disk_.has_value()) {
213 return *crc_from_disk_;
214 }
215 auto data = InitializeData();
216 if (!data) {
217 return data.GetError();
218 }
219 return (*data)->crc;
220}
221
Ryan Mitchell6a2ca782021-01-19 13:51:15 -0800222Result<Unit> FabricatedOverlay::ToBinaryStream(std::ostream& stream) const {
Ryan Mitchell2ed8bfa2021-01-08 13:34:28 -0800223 auto data = InitializeData();
224 if (!data) {
225 return data.GetError();
226 }
227
228 Write32(stream, kFabricatedOverlayMagic);
229 Write32(stream, kFabricatedOverlayCurrentVersion);
230 Write32(stream, (*data)->crc);
231 stream.write(reinterpret_cast<const char*>((*data)->data.get()), (*data)->data_size);
232 if (stream.bad()) {
233 return Error("Failed to write serialized fabricated overlay.");
234 }
235
236 return Unit{};
237}
238
239using FabContainer = FabricatedOverlayContainer;
240FabContainer::FabricatedOverlayContainer(FabricatedOverlay&& overlay, std::string&& path)
241 : overlay_(std::forward<FabricatedOverlay>(overlay)), path_(std::forward<std::string>(path)) {
242}
243
244FabContainer::~FabricatedOverlayContainer() = default;
245
246Result<std::unique_ptr<FabContainer>> FabContainer::FromPath(std::string path) {
247 std::fstream fin(path);
248 auto overlay = FabricatedOverlay::FromBinaryStream(fin);
249 if (!overlay) {
250 return overlay.GetError();
251 }
252 return std::unique_ptr<FabContainer>(
253 new FabricatedOverlayContainer(std::move(*overlay), std::move(path)));
254}
255
256std::unique_ptr<FabricatedOverlayContainer> FabContainer::FromOverlay(FabricatedOverlay&& overlay) {
257 return std::unique_ptr<FabContainer>(
258 new FabricatedOverlayContainer(std::move(overlay), {} /* path */));
259}
260
Ryan Mitchell6a2ca782021-01-19 13:51:15 -0800261OverlayManifestInfo FabContainer::GetManifestInfo() const {
Ryan Mitchell2ed8bfa2021-01-08 13:34:28 -0800262 const pb::FabricatedOverlay& overlay_pb = overlay_.overlay_pb_;
Ryan Mitchell2ed8bfa2021-01-08 13:34:28 -0800263 return OverlayManifestInfo{
Ryan Mitchell6a2ca782021-01-19 13:51:15 -0800264 .package_name = overlay_pb.package_name(),
Ryan Mitchell2ed8bfa2021-01-08 13:34:28 -0800265 .name = overlay_pb.name(),
266 .target_package = overlay_pb.target_package_name(),
267 .target_name = overlay_pb.target_overlayable(),
268 };
269}
270
Ryan Mitchell6a2ca782021-01-19 13:51:15 -0800271Result<OverlayManifestInfo> FabContainer::FindOverlayInfo(const std::string& name) const {
272 const OverlayManifestInfo info = GetManifestInfo();
273 if (name != info.name) {
274 return Error("Failed to find name '%s' in fabricated overlay", name.c_str());
275 }
276 return info;
277}
278
Ryan Mitchell2ed8bfa2021-01-08 13:34:28 -0800279Result<OverlayData> FabContainer::GetOverlayData(const OverlayManifestInfo& info) const {
280 const pb::FabricatedOverlay& overlay_pb = overlay_.overlay_pb_;
281 if (info.name != overlay_pb.name()) {
282 return Error("Failed to find name '%s' in fabricated overlay", info.name.c_str());
283 }
284
285 OverlayData result{};
286 for (const auto& package : overlay_pb.packages()) {
287 for (const auto& type : package.types()) {
288 for (const auto& entry : type.entries()) {
289 auto name = base::StringPrintf("%s:%s/%s", package.name().c_str(), type.name().c_str(),
290 entry.name().c_str());
291 const auto& res_value = entry.res_value();
292 result.pairs.emplace_back(OverlayData::Value{
293 name, TargetValue{.data_type = static_cast<uint8_t>(res_value.data_type()),
294 .data_value = res_value.data_value()}});
295 }
296 }
297 }
298 return result;
299}
300
301Result<uint32_t> FabContainer::GetCrc() const {
302 return overlay_.GetCrc();
303}
304
305const std::string& FabContainer::GetPath() const {
306 return path_;
307}
308
309Result<std::string> FabContainer::GetResourceName(ResourceId /* id */) const {
310 return Error("Fabricated overlay does not contain resources.");
311}
312
Mårten Kongstad1195a6b2021-05-11 12:57:01 +0000313} // namespace android::idmap2