1 /* 2 * Copyright (C) 2016 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 #ifndef LOADEDARSC_H_ 18 #define LOADEDARSC_H_ 19 20 #include <map> 21 #include <memory> 22 #include <set> 23 #include <vector> 24 #include <unordered_map> 25 #include <unordered_set> 26 27 #include <android-base/macros.h> 28 #include <android-base/result.h> 29 30 #include "androidfw/ByteBucketArray.h" 31 #include "androidfw/Chunk.h" 32 #include "androidfw/Idmap.h" 33 #include "androidfw/ResourceTypes.h" 34 #include "androidfw/Util.h" 35 36 namespace android { 37 38 class DynamicPackageEntry { 39 public: 40 DynamicPackageEntry() = default; DynamicPackageEntry(std::string && package_name,int package_id)41 DynamicPackageEntry(std::string&& package_name, int package_id) 42 : package_name(std::move(package_name)), package_id(package_id) {} 43 44 std::string package_name; 45 int package_id = 0; 46 }; 47 48 // TypeSpec is going to be immediately proceeded by 49 // an array of Type structs, all in the same block of memory. 50 struct TypeSpec { 51 struct TypeEntry { 52 incfs::verified_map_ptr<ResTable_type> type; 53 54 // Type configurations are accessed frequently when setting up an AssetManager and querying 55 // resources. Access this cached configuration to minimize page faults. 56 ResTable_config config; 57 }; 58 59 // Pointer to the mmapped data where flags are kept. Flags denote whether the resource entry is 60 // public and under which configurations it varies. 61 incfs::verified_map_ptr<ResTable_typeSpec> type_spec; 62 63 std::vector<TypeEntry> type_entries; 64 GetFlagsForEntryIndexTypeSpec65 base::expected<uint32_t, NullOrIOError> GetFlagsForEntryIndex(uint16_t entry_index) const { 66 if (entry_index >= dtohl(type_spec->entryCount)) { 67 return 0U; 68 } 69 const auto entry_flags_ptr = ((type_spec + 1).convert<uint32_t>() + entry_index); 70 if (!entry_flags_ptr) { 71 return base::unexpected(IOError::PAGES_MISSING); 72 } 73 return entry_flags_ptr.value(); 74 } 75 }; 76 77 // Flags that change the behavior of loaded packages. 78 // Keep in sync with f/b/android/content/res/ApkAssets.java 79 using package_property_t = uint32_t; 80 enum : package_property_t { 81 // The package contains framework resource values specified by the system. 82 // This allows some functions to filter out this package when computing 83 // what configurations/resources are available. 84 PROPERTY_SYSTEM = 1U << 0U, 85 86 // The package is a shared library or has a package id of 7f and is loaded as a shared library by 87 // force. 88 PROPERTY_DYNAMIC = 1U << 1U, 89 90 // The package has been loaded dynamically using a ResourcesProvider. 91 PROPERTY_LOADER = 1U << 2U, 92 93 // The package is a RRO. 94 PROPERTY_OVERLAY = 1U << 3U, 95 96 // The apk assets is owned by the application running in this process and incremental crash 97 // protections for this APK must be disabled. 98 PROPERTY_DISABLE_INCREMENTAL_HARDENING = 1U << 4U, 99 100 // The apk assets only contain the overlayable declarations information. 101 PROPERTY_ONLY_OVERLAYABLES = 1U << 5U, 102 103 // Optimize the resource lookups by name via an in-memory lookup table. 104 PROPERTY_OPTIMIZE_NAME_LOOKUPS = 1U << 6U, 105 }; 106 107 struct OverlayableInfo { 108 std::string_view name; 109 std::string_view actor; 110 uint32_t policy_flags; 111 }; 112 113 class LoadedPackage { 114 public: 115 class iterator { 116 public: 117 iterator& operator=(const iterator& rhs) { 118 loadedPackage_ = rhs.loadedPackage_; 119 typeIndex_ = rhs.typeIndex_; 120 entryIndex_ = rhs.entryIndex_; 121 return *this; 122 } 123 124 bool operator==(const iterator& rhs) const { 125 return loadedPackage_ == rhs.loadedPackage_ && 126 typeIndex_ == rhs.typeIndex_ && 127 entryIndex_ == rhs.entryIndex_; 128 } 129 130 bool operator!=(const iterator& rhs) const { 131 return !(*this == rhs); 132 } 133 134 iterator operator++(int) { 135 size_t prevTypeIndex_ = typeIndex_; 136 size_t prevEntryIndex_ = entryIndex_; 137 operator++(); 138 return iterator(loadedPackage_, prevTypeIndex_, prevEntryIndex_); 139 } 140 141 iterator& operator++(); 142 143 uint32_t operator*() const; 144 145 private: 146 friend class LoadedPackage; 147 148 iterator(const LoadedPackage* lp, size_t ti, size_t ei); 149 150 const LoadedPackage* loadedPackage_; 151 size_t typeIndex_; 152 size_t entryIndex_; 153 const size_t typeIndexEnd_; // STL style end, so one past the last element 154 }; 155 begin()156 iterator begin() const { 157 return iterator(this, 0, 0); 158 } 159 end()160 iterator end() const { 161 return iterator(this, resource_ids_.size() + 1, 0); 162 } 163 164 static std::unique_ptr<const LoadedPackage> Load(const Chunk& chunk, 165 package_property_t property_flags); 166 167 // Finds the entry with the specified type name and entry name. The names are in UTF-16 because 168 // the underlying ResStringPool API expects this. For now this is acceptable, but since 169 // the default policy in AAPT2 is to build UTF-8 string pools, this needs to change. 170 // Returns a partial resource ID, with the package ID left as 0x00. The caller is responsible 171 // for patching the correct package ID to the resource ID. 172 base::expected<uint32_t, NullOrIOError> FindEntryByName(const std::u16string& type_name, 173 const std::u16string& entry_name) const; 174 175 static base::expected<incfs::verified_map_ptr<ResTable_entry>, NullOrIOError> 176 GetEntry(incfs::verified_map_ptr<ResTable_type> type_chunk, uint16_t entry_index); 177 178 static base::expected<uint32_t, NullOrIOError> GetEntryOffset( 179 incfs::verified_map_ptr<ResTable_type> type_chunk, uint16_t entry_index); 180 181 static base::expected<incfs::verified_map_ptr<ResTable_entry>, NullOrIOError> 182 GetEntryFromOffset(incfs::verified_map_ptr<ResTable_type> type_chunk, uint32_t offset); 183 184 // Returns the string pool where type names are stored. GetTypeStringPool()185 const ResStringPool* GetTypeStringPool() const { 186 return &type_string_pool_; 187 } 188 189 // Returns the string pool where the names of resource entries are stored. GetKeyStringPool()190 const ResStringPool* GetKeyStringPool() const { 191 return &key_string_pool_; 192 } 193 GetPackageName()194 const std::string& GetPackageName() const { 195 return package_name_; 196 } 197 GetPackageId()198 int GetPackageId() const { 199 return package_id_; 200 } 201 202 // Returns true if this package is dynamic (shared library) and needs to have an ID assigned. IsDynamic()203 bool IsDynamic() const { 204 return (property_flags_ & PROPERTY_DYNAMIC) != 0; 205 } 206 207 // Returns true if this package is a Runtime Resource Overlay. IsOverlay()208 bool IsOverlay() const { 209 return (property_flags_ & PROPERTY_OVERLAY) != 0; 210 } 211 212 // Returns true if this package originates from a system provided resource. IsSystem()213 bool IsSystem() const { 214 return (property_flags_ & PROPERTY_SYSTEM) != 0; 215 } 216 217 // Returns true if this package is a custom loader and should behave like an overlay. IsCustomLoader()218 bool IsCustomLoader() const { 219 return (property_flags_ & PROPERTY_LOADER) != 0; 220 } 221 GetPropertyFlags()222 package_property_t GetPropertyFlags() const { 223 return property_flags_; 224 } 225 226 // Returns the map of package name to package ID used in this LoadedPackage. At runtime, a 227 // package could have been assigned a different package ID than what this LoadedPackage was 228 // compiled with. AssetManager rewrites the package IDs so that they are compatible at runtime. GetDynamicPackageMap()229 const std::vector<DynamicPackageEntry>& GetDynamicPackageMap() const { 230 return dynamic_package_map_; 231 } 232 233 // Populates a set of ResTable_config structs, possibly excluding configurations defined for 234 // the mipmap type. 235 base::expected<std::monostate, IOError> CollectConfigurations( 236 bool exclude_mipmap, std::set<ResTable_config>* out_configs) const; 237 238 // Populates a set of strings representing locales. 239 // If `canonicalize` is set to true, each locale is transformed into its canonical format 240 // before being inserted into the set. This may cause some equivalent locales to de-dupe. 241 void CollectLocales(bool canonicalize, std::set<std::string>* out_locales) const; 242 243 // type_idx is TT - 1 from 0xPPTTEEEE. GetTypeSpecByTypeIndex(uint8_t type_index)244 inline const TypeSpec* GetTypeSpecByTypeIndex(uint8_t type_index) const { 245 // If the type IDs are offset in this package, we need to take that into account when searching 246 // for a type. 247 const auto& type_spec = type_specs_.find(type_index + 1 - type_id_offset_); 248 if (type_spec == type_specs_.end()) { 249 return nullptr; 250 } 251 return &type_spec->second; 252 } 253 254 template <typename Func> ForEachTypeSpec(Func f)255 void ForEachTypeSpec(Func f) const { 256 for (const auto& type_spec : type_specs_) { 257 f(type_spec.second, type_spec.first); 258 } 259 } 260 261 // Retrieves the overlayable properties of the specified resource. If the resource is not 262 // overlayable, this will return a null pointer. GetOverlayableInfo(uint32_t resid)263 const OverlayableInfo* GetOverlayableInfo(uint32_t resid) const { 264 for (const std::pair<OverlayableInfo, std::unordered_set<uint32_t>>& overlayable_info_ids 265 : overlayable_infos_) { 266 if (overlayable_info_ids.second.find(resid) != overlayable_info_ids.second.end()) { 267 return &overlayable_info_ids.first; 268 } 269 } 270 return nullptr; 271 } 272 273 // Retrieves whether or not the package defines overlayable resources. 274 // TODO(123905379): Remove this when the enforcement of overlayable is turned on for all APK and 275 // not just those that defined overlayable resources. DefinesOverlayable()276 bool DefinesOverlayable() const { 277 return defines_overlayable_; 278 } 279 GetOverlayableMap()280 const std::unordered_map<std::string, std::string>& GetOverlayableMap() const { 281 return overlayable_map_; 282 } 283 GetAliasResourceIdMap()284 const std::vector<std::pair<uint32_t, uint32_t>>& GetAliasResourceIdMap() const { 285 return alias_id_map_; 286 } 287 288 private: 289 DISALLOW_COPY_AND_ASSIGN(LoadedPackage); 290 291 explicit LoadedPackage(bool optimize_name_lookups = false) type_string_pool_(optimize_name_lookups)292 : type_string_pool_(optimize_name_lookups), key_string_pool_(optimize_name_lookups) { 293 } 294 295 ResStringPool type_string_pool_; 296 ResStringPool key_string_pool_; 297 std::string package_name_; 298 bool defines_overlayable_ = false; 299 int package_id_ = -1; 300 int type_id_offset_ = 0; 301 package_property_t property_flags_ = 0U; 302 303 std::unordered_map<uint8_t, TypeSpec> type_specs_; 304 ByteBucketArray<uint32_t> resource_ids_; 305 std::vector<DynamicPackageEntry> dynamic_package_map_; 306 std::vector<std::pair<OverlayableInfo, std::unordered_set<uint32_t>>> overlayable_infos_; 307 std::vector<std::pair<uint32_t, uint32_t>> alias_id_map_; 308 309 // A map of overlayable name to actor 310 std::unordered_map<std::string, std::string> overlayable_map_; 311 }; 312 313 // Read-only view into a resource table. This class validates all data 314 // when loading, including offsets and lengths. 315 class LoadedArsc { 316 public: 317 // Load a resource table from memory pointed to by `data` of size `len`. 318 // The lifetime of `data` must out-live the LoadedArsc returned from this method. 319 320 static std::unique_ptr<LoadedArsc> Load(incfs::map_ptr<void> data, 321 size_t length, 322 const LoadedIdmap* loaded_idmap = nullptr, 323 package_property_t property_flags = 0U); 324 325 static std::unique_ptr<LoadedArsc> Load(const LoadedIdmap* loaded_idmap = nullptr); 326 327 // Create an empty LoadedArsc. This is used when an APK has no resources.arsc. 328 static std::unique_ptr<LoadedArsc> CreateEmpty(); 329 330 // Returns the string pool where all string resource values 331 // (Res_value::dataType == Res_value::TYPE_STRING) are indexed. GetStringPool()332 inline const ResStringPool* GetStringPool() const { 333 return global_string_pool_.get(); 334 } 335 336 // Gets a pointer to the package with the specified package ID, or nullptr if no such package 337 // exists. 338 const LoadedPackage* GetPackageById(uint8_t package_id) const; 339 340 // Returns a vector of LoadedPackage pointers, representing the packages in this LoadedArsc. GetPackages()341 inline const std::vector<std::unique_ptr<const LoadedPackage>>& GetPackages() const { 342 return packages_; 343 } 344 345 private: 346 DISALLOW_COPY_AND_ASSIGN(LoadedArsc); 347 348 LoadedArsc() = default; 349 bool LoadTable( 350 const Chunk& chunk, const LoadedIdmap* loaded_idmap, package_property_t property_flags); 351 bool LoadStringPool(const LoadedIdmap* loaded_idmap); 352 353 std::unique_ptr<ResStringPool> global_string_pool_ = util::make_unique<ResStringPool>(); 354 std::vector<std::unique_ptr<const LoadedPackage>> packages_; 355 }; 356 357 } // namespace android 358 359 #endif /* LOADEDARSC_H_ */ 360