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 /*
18  * # idmap file format (current version)
19  *
20  * idmap             := header data*
21  * header            := magic version target_crc overlay_crc target_path overlay_path debug_info
22  * data              := data_header data_block*
23  * data_header       := target_package_id types_count
24  * data_block        := target_type overlay_type entry_count entry_offset entry*
25  * overlay_path      := string256
26  * target_path       := string256
27  * debug_info        := string
28  * string            := <uint32_t> <uint8_t>+ '\0'+
29  * entry             := <uint32_t>
30  * entry_count       := <uint16_t>
31  * entry_offset      := <uint16_t>
32  * magic             := <uint32_t>
33  * overlay_crc       := <uint32_t>
34  * overlay_type      := <uint16_t>
35  * string256         := <uint8_t>[256]
36  * target_crc        := <uint32_t>
37  * target_package_id := <uint16_t>
38  * target_type       := <uint16_t>
39  * types_count       := <uint16_t>
40  * version           := <uint32_t>
41  *
42  *
43  * # idmap file format changelog
44  * ## v1
45  * - Identical to idmap v1.
46  *
47  * ## v2
48  * - Entries are no longer separated by type into type specific data blocks.
49  * - Added overlay-indexed target resource id lookup capabilities.
50  * - Target and overlay entries are stored as a sparse array in the data block. The target entries
51  *   array maps from target resource id to overlay data type and value and the array is sorted by
52  *   target resource id. The overlay entries array maps from overlay resource id to target resource
53  *   id and the array is sorted by overlay resource id. It is important for both arrays to be sorted
54  *   to allow for O(log(number_of_overlaid_resources)) performance when looking up resource
55  *   mappings at runtime.
56  * - Idmap can now encode a type and value to override a resource without needing a table entry.
57  * - A string pool block is included to retrieve the value of strings that do not have a resource
58  *   table entry.
59  *
60  * ## v3
61  * - Add 'debug' block to IdmapHeader.
62  */
63 
64 #ifndef IDMAP2_INCLUDE_IDMAP2_IDMAP_H_
65 #define IDMAP2_INCLUDE_IDMAP2_IDMAP_H_
66 
67 #include <iostream>
68 #include <memory>
69 #include <string>
70 #include <vector>
71 
72 #include "android-base/macros.h"
73 #include "androidfw/ApkAssets.h"
74 #include "androidfw/ResourceTypes.h"
75 #include "androidfw/StringPiece.h"
76 #include "idmap2/ResourceMapping.h"
77 #include "idmap2/ZipFile.h"
78 
79 namespace android::idmap2 {
80 
81 class Idmap;
82 class Visitor;
83 
84 static constexpr const ResourceId kPadding = 0xffffffffu;
85 static constexpr const EntryId kNoEntry = 0xffffu;
86 
87 // magic number: all idmap files start with this
88 static constexpr const uint32_t kIdmapMagic = android::kIdmapMagic;
89 
90 // current version of the idmap binary format; must be incremented when the format is changed
91 static constexpr const uint32_t kIdmapCurrentVersion = android::kIdmapCurrentVersion;
92 
93 // strings in the idmap are encoded char arrays of length 'kIdmapStringLength' (including mandatory
94 // terminating null)
95 static constexpr const size_t kIdmapStringLength = 256;
96 
97 // Retrieves a crc generated using all of the files within the zip that can affect idmap generation.
98 Result<uint32_t> GetPackageCrc(const ZipFile& zip_info);
99 
100 class IdmapHeader {
101  public:
102   static std::unique_ptr<const IdmapHeader> FromBinaryStream(std::istream& stream);
103 
GetMagic()104   inline uint32_t GetMagic() const {
105     return magic_;
106   }
107 
GetVersion()108   inline uint32_t GetVersion() const {
109     return version_;
110   }
111 
GetTargetCrc()112   inline uint32_t GetTargetCrc() const {
113     return target_crc_;
114   }
115 
GetOverlayCrc()116   inline uint32_t GetOverlayCrc() const {
117     return overlay_crc_;
118   }
119 
GetFulfilledPolicies()120   inline uint32_t GetFulfilledPolicies() const {
121     return fulfilled_policies_;
122   }
123 
GetEnforceOverlayable()124   bool GetEnforceOverlayable() const {
125     return enforce_overlayable_;
126   }
127 
GetTargetPath()128   inline StringPiece GetTargetPath() const {
129     return StringPiece(target_path_);
130   }
131 
GetOverlayPath()132   inline StringPiece GetOverlayPath() const {
133     return StringPiece(overlay_path_);
134   }
135 
GetDebugInfo()136   inline const std::string& GetDebugInfo() const {
137     return debug_info_;
138   }
139 
140   // Invariant: anytime the idmap data encoding is changed, the idmap version
141   // field *must* be incremented. Because of this, we know that if the idmap
142   // header is up-to-date the entire file is up-to-date.
143   Result<Unit> IsUpToDate(const char* target_path, const char* overlay_path,
144                           PolicyBitmask fulfilled_policies, bool enforce_overlayable) const;
145   Result<Unit> IsUpToDate(const char* target_path, const char* overlay_path, uint32_t target_crc,
146                           uint32_t overlay_crc, PolicyBitmask fulfilled_policies,
147                           bool enforce_overlayable) const;
148 
149   void accept(Visitor* v) const;
150 
151  private:
IdmapHeader()152   IdmapHeader() {
153   }
154 
155   uint32_t magic_;
156   uint32_t version_;
157   uint32_t target_crc_;
158   uint32_t overlay_crc_;
159   uint32_t fulfilled_policies_;
160   bool enforce_overlayable_;
161   char target_path_[kIdmapStringLength];
162   char overlay_path_[kIdmapStringLength];
163   std::string debug_info_;
164 
165   friend Idmap;
166   DISALLOW_COPY_AND_ASSIGN(IdmapHeader);
167 };
168 class IdmapData {
169  public:
170   class Header {
171    public:
172     static std::unique_ptr<const Header> FromBinaryStream(std::istream& stream);
173 
GetTargetPackageId()174     inline PackageId GetTargetPackageId() const {
175       return target_package_id_;
176     }
177 
GetOverlayPackageId()178     inline PackageId GetOverlayPackageId() const {
179       return overlay_package_id_;
180     }
181 
GetTargetEntryCount()182     inline uint32_t GetTargetEntryCount() const {
183       return target_entry_count;
184     }
185 
GetOverlayEntryCount()186     inline uint32_t GetOverlayEntryCount() const {
187       return overlay_entry_count;
188     }
189 
GetStringPoolIndexOffset()190     inline uint32_t GetStringPoolIndexOffset() const {
191       return string_pool_index_offset;
192     }
193 
GetStringPoolLength()194     inline uint32_t GetStringPoolLength() const {
195       return string_pool_len;
196     }
197 
198     void accept(Visitor* v) const;
199 
200    private:
201     PackageId target_package_id_;
202     PackageId overlay_package_id_;
203     uint32_t target_entry_count;
204     uint32_t overlay_entry_count;
205     uint32_t string_pool_index_offset;
206     uint32_t string_pool_len;
207     Header() = default;
208 
209     friend Idmap;
210     friend IdmapData;
211     DISALLOW_COPY_AND_ASSIGN(Header);
212   };
213 
214   struct TargetEntry {
215     ResourceId target_id;
216     TargetValue::DataType data_type;
217     TargetValue::DataValue data_value;
218   };
219 
220   struct OverlayEntry {
221     ResourceId overlay_id;
222     ResourceId target_id;
223   };
224 
225   static std::unique_ptr<const IdmapData> FromBinaryStream(std::istream& stream);
226 
227   static Result<std::unique_ptr<const IdmapData>> FromResourceMapping(
228       const ResourceMapping& resource_mapping);
229 
GetHeader()230   inline const std::unique_ptr<const Header>& GetHeader() const {
231     return header_;
232   }
233 
GetTargetEntries()234   inline const std::vector<TargetEntry>& GetTargetEntries() const {
235     return target_entries_;
236   }
237 
GetOverlayEntries()238   inline const std::vector<OverlayEntry>& GetOverlayEntries() const {
239     return overlay_entries_;
240   }
241 
GetStringPoolData()242   inline const void* GetStringPoolData() const {
243     return string_pool_.get();
244   }
245 
246   void accept(Visitor* v) const;
247 
248  private:
IdmapData()249   IdmapData() {
250   }
251 
252   std::unique_ptr<const Header> header_;
253   std::vector<TargetEntry> target_entries_;
254   std::vector<OverlayEntry> overlay_entries_;
255   std::unique_ptr<uint8_t[]> string_pool_;
256 
257   friend Idmap;
258   DISALLOW_COPY_AND_ASSIGN(IdmapData);
259 };
260 
261 class Idmap {
262  public:
263   static std::string CanonicalIdmapPathFor(const std::string& absolute_dir,
264                                            const std::string& absolute_apk_path);
265 
266   static Result<std::unique_ptr<const Idmap>> FromBinaryStream(std::istream& stream);
267 
268   // In the current version of idmap, the first package in each resources.arsc
269   // file is used; change this in the next version of idmap to use a named
270   // package instead; also update FromApkAssets to take additional parameters:
271   // the target and overlay package names
272   static Result<std::unique_ptr<const Idmap>> FromApkAssets(const ApkAssets& target_apk_assets,
273                                                             const ApkAssets& overlay_apk_assets,
274                                                             const PolicyBitmask& fulfilled_policies,
275                                                             bool enforce_overlayable);
276 
GetHeader()277   inline const std::unique_ptr<const IdmapHeader>& GetHeader() const {
278     return header_;
279   }
280 
GetData()281   inline const std::vector<std::unique_ptr<const IdmapData>>& GetData() const {
282     return data_;
283   }
284 
285   void accept(Visitor* v) const;
286 
287  private:
Idmap()288   Idmap() {
289   }
290 
291   std::unique_ptr<const IdmapHeader> header_;
292   std::vector<std::unique_ptr<const IdmapData>> data_;
293 
294   DISALLOW_COPY_AND_ASSIGN(Idmap);
295 };
296 
297 class Visitor {
298  public:
~Visitor()299   virtual ~Visitor() {
300   }
301   virtual void visit(const Idmap& idmap) = 0;
302   virtual void visit(const IdmapHeader& header) = 0;
303   virtual void visit(const IdmapData& data) = 0;
304   virtual void visit(const IdmapData::Header& header) = 0;
305 };
306 
307 }  // namespace android::idmap2
308 
309 #endif  // IDMAP2_INCLUDE_IDMAP2_IDMAP_H_
310