1 package org.robolectric.res.android; 2 3 // transliterated from 4 // https://android.googlesource.com/platform/frameworks/base/+/android-9.0.0_r12/libs/androidfw/Idmap.cpp and 5 // https://android.googlesource.com/platform/frameworks/base/+/android-9.0.0_r12/libs/androidfw/include/androidfw/Idmap.h 6 7 import static org.robolectric.res.android.Util.ATRACE_CALL; 8 import static org.robolectric.res.android.Util.SIZEOF_CPTR; 9 import static org.robolectric.res.android.Util.SIZEOF_INT; 10 import static org.robolectric.res.android.Util.dtohl; 11 import static org.robolectric.res.android.Util.dtohs; 12 import static org.robolectric.res.android.Util.logError; 13 14 import java.util.HashMap; 15 import java.util.Map; 16 import org.robolectric.res.android.ResourceTypes.IdmapEntry_header; 17 import org.robolectric.res.android.ResourceTypes.Idmap_header; 18 19 // #define ATRACE_TAG ATRACE_TAG_RESOURCES 20 // 21 // #include "androidfw/Idmap.h" 22 // 23 // #include "android-base/logging.h" 24 // #include "android-base/stringprintf.h" 25 // #include "utils/ByteOrder.h" 26 // #include "utils/Trace.h" 27 // 28 // #ifdef _WIN32 29 // #ifdef ERROR 30 // #undef ERROR 31 // #endif 32 // #endif 33 // 34 // #include "androidfw/ResourceTypes.h" 35 // 36 // using ::android::base::StringPrintf; 37 // 38 // namespace android { 39 class Idmap { 40 is_valid_package_id(short id)41 static boolean is_valid_package_id(short id) { 42 return id != 0 && id <= 255; 43 } 44 is_valid_type_id(short id)45 static boolean is_valid_type_id(short id) { 46 // Type IDs and package IDs have the same constraints in the IDMAP. 47 return is_valid_package_id(id); 48 } 49 50 // Represents a loaded/parsed IDMAP for a Runtime Resource Overlay (RRO). 51 // An RRO and its target APK have different resource IDs assigned to their resources. Overlaying 52 // a resource is done by resource name. An IDMAP is a generated mapping between the resource IDs 53 // of the RRO and the target APK for each resource with the same name. 54 // A LoadedIdmap can be set alongside the overlay's LoadedArsc to allow the overlay ApkAssets to 55 // masquerade as the target ApkAssets resources. 56 static class LoadedIdmap { 57 Idmap_header header_ = null; 58 String overlay_apk_path_; 59 final Map<Byte, IdmapEntry_header> type_map_ = new HashMap<>(); 60 LoadedIdmap(Idmap_header header_)61 public LoadedIdmap(Idmap_header header_) { 62 this.header_ = header_; 63 } 64 65 // Performs a lookup of the expected entry ID for the given IDMAP entry header. 66 // Returns true if the mapping exists and fills `output_entry_id` with the result. Lookup( IdmapEntry_header header, int input_entry_id, final Ref<Integer> output_entry_id)67 static boolean Lookup( 68 IdmapEntry_header header, int input_entry_id, final Ref<Integer> output_entry_id) { 69 if (input_entry_id < dtohs(header.entry_id_offset)) { 70 // After applying the offset, the entry is not present. 71 return false; 72 } 73 74 input_entry_id -= dtohs(header.entry_id_offset); 75 if (input_entry_id >= dtohs(header.entry_count)) { 76 // The entry is not present. 77 return false; 78 } 79 80 int result = dtohl(header.entries[input_entry_id]); 81 if (result == 0xffffffff) { 82 return false; 83 } 84 output_entry_id.set(result); 85 return true; 86 } 87 is_word_aligned(int offset)88 static boolean is_word_aligned(int offset) { 89 return (offset & 0x03) == 0; 90 } 91 92 @SuppressWarnings("DoNotCallSuggester") IsValidIdmapHeader(StringPiece data)93 static boolean IsValidIdmapHeader(StringPiece data) { 94 throw new UnsupportedOperationException(); 95 // if (!is_word_aligned(data.data())) { 96 // LOG(ERROR) << "Idmap header is not word aligned."; 97 // return false; 98 // } 99 // 100 // if (data.size() < sizeof(Idmap_header)) { 101 // LOG(ERROR) << "Idmap header is too small."; 102 // return false; 103 // } 104 // 105 // const Idmap_header* header = reinterpret_cast<const Idmap_header*>(data.data()); 106 // if (dtohl(header->magic) != kIdmapMagic) { 107 // LOG(ERROR) << StringPrintf("Invalid Idmap file: bad magic value (was 0x%08x, expected 108 // 0x%08x)", 109 // dtohl(header->magic), kIdmapMagic); 110 // return false; 111 // } 112 // 113 // if (dtohl(header->version) != kIdmapCurrentVersion) { 114 // // We are strict about versions because files with this format are auto-generated and 115 // don't need 116 // // backwards compatibility. 117 // LOG(ERROR) << StringPrintf("Version mismatch in Idmap (was 0x%08x, expected 0x%08x)", 118 // dtohl(header->version), kIdmapCurrentVersion); 119 // return false; 120 // } 121 // 122 // if (!is_valid_package_id(dtohs(header->target_package_id))) { 123 // LOG(ERROR) << StringPrintf("Target package ID in Idmap is invalid: 0x%02x", 124 // dtohs(header->target_package_id)); 125 // return false; 126 // } 127 // 128 // if (dtohs(header->type_count) > 255) { 129 // LOG(ERROR) << StringPrintf("Idmap has too many type mappings (was %d, max 255)", 130 // (int)dtohs(header->type_count)); 131 // return false; 132 // } 133 // return true; 134 } 135 136 // LoadedIdmap::LoadedIdmap(const Idmap_header* header) : header_(header) { 137 // size_t length = strnlen(reinterpret_cast<const char*>(header_->overlay_path), 138 // arraysize(header_->overlay_path)); 139 // overlay_apk_path_.assign(reinterpret_cast<const char*>(header_->overlay_path), length); 140 // } 141 // Loads an IDMAP from a chunk of memory. Returns nullptr if the IDMAP data was malformed. Load(StringPiece idmap_data)142 LoadedIdmap Load(StringPiece idmap_data) { 143 ATRACE_CALL(); 144 if (!IsValidIdmapHeader(idmap_data)) { 145 return emptyBraces(); 146 } 147 148 // Idmap_header header = reinterpret_cast<const Idmap_header*>(idmap_data.data()); 149 Idmap_header header = idmap_data.asIdmap_header(); 150 151 // Can't use make_unique because LoadedImpl constructor is private. 152 LoadedIdmap loaded_idmap = new LoadedIdmap(header); 153 154 // const byte* data_ptr = reinterpret_cast<const byte*>(idmap_data.data()) + sizeof(*header); 155 StringPiece data_ptr = 156 new StringPiece(idmap_data.myBuf(), idmap_data.myOffset() + SIZEOF_CPTR); 157 // int data_size = idmap_data.size() - sizeof(*header); 158 int data_size = idmap_data.size() - SIZEOF_CPTR; 159 160 int type_maps_encountered = 0; 161 while (data_size >= IdmapEntry_header.SIZEOF) { 162 if (!is_word_aligned(data_ptr.myOffset())) { 163 logError("Type mapping in Idmap is not word aligned"); 164 return emptyBraces(); 165 } 166 167 // Validate the type IDs. 168 // IdmapEntry_header entry_header = reinterpret_cast<const IdmapEntry_header*>(data_ptr); 169 IdmapEntry_header entry_header = 170 new IdmapEntry_header(data_ptr.myBuf(), data_ptr.myOffset()); 171 if (!is_valid_type_id(dtohs(entry_header.target_type_id)) 172 || !is_valid_type_id(dtohs(entry_header.overlay_type_id))) { 173 logError( 174 String.format( 175 "Invalid type map (0x%02x -> 0x%02x)", 176 dtohs(entry_header.target_type_id), dtohs(entry_header.overlay_type_id))); 177 return emptyBraces(); 178 } 179 180 // Make sure there is enough space for the entries declared in the header. 181 if ((data_size - SIZEOF_CPTR) / SIZEOF_INT < dtohs(entry_header.entry_count)) { 182 logError( 183 String.format( 184 "Idmap too small for the number of entries (%d)", 185 (int) dtohs(entry_header.entry_count))); 186 return emptyBraces(); 187 } 188 189 // Only add a non-empty overlay. 190 if (dtohs(entry_header.entry_count) != 0) { 191 // loaded_idmap.type_map_[static_cast<byte>(dtohs(entry_header.overlay_type_id))] = 192 // entry_header; 193 loaded_idmap.type_map_.put((byte) dtohs(entry_header.overlay_type_id), entry_header); 194 } 195 196 // int entry_size_bytes = 197 // sizeof(*entry_header) + (dtohs(entry_header.entry_count) * SIZEOF_INT); 198 int entry_size_bytes = SIZEOF_CPTR + (dtohs(entry_header.entry_count) * SIZEOF_INT); 199 data_ptr = new StringPiece(data_ptr.myBuf(), data_ptr.myOffset() + entry_size_bytes); 200 data_size -= entry_size_bytes; 201 type_maps_encountered++; 202 } 203 204 // Verify that we parsed all the type maps. 205 if (type_maps_encountered != dtohs(header.type_count)) { 206 logError( 207 "Parsed " 208 + type_maps_encountered 209 + " type maps but expected " 210 + (int) dtohs(header.type_count)); 211 return emptyBraces(); 212 } 213 // return std.move(loaded_idmap); 214 return loaded_idmap; 215 } 216 emptyBraces()217 private LoadedIdmap emptyBraces() { 218 return new LoadedIdmap(null); 219 } 220 221 // Returns the package ID for which this overlay should apply. TargetPackageId()222 int TargetPackageId() { 223 return dtohs(header_.target_package_id); 224 } 225 226 // Returns the path to the RRO (Runtime Resource Overlay) APK for which this IDMAP was 227 // generated. OverlayApkPath()228 String OverlayApkPath() { 229 return overlay_apk_path_; 230 } 231 232 // Returns the mapping of target entry ID to overlay entry ID for the given target type. GetEntryMapForType(byte type_id)233 IdmapEntry_header GetEntryMapForType(byte type_id) { 234 // auto iter = type_map_.find(type_id); 235 // if (iter != type_map_.end()) { 236 // return iter.second; 237 // } 238 // return null; 239 return type_map_.get(type_id); 240 } 241 // 242 // } // namespace android 243 } 244 } 245