1 /* 2 * Copyright (C) 2015 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 "compile/IdAssigner.h" 18 19 #include <map> 20 21 #include "android-base/logging.h" 22 23 #include "ResourceTable.h" 24 #include "process/IResourceTableConsumer.h" 25 #include "util/Util.h" 26 27 namespace aapt { 28 29 /** 30 * Assigns the intended ID to the ResourceTablePackage, ResourceTableType, and 31 * ResourceEntry, 32 * as long as there is no existing ID or the ID is the same. 33 */ 34 static bool AssignId(IDiagnostics* diag, const ResourceId& id, 35 const ResourceName& name, ResourceTablePackage* pkg, 36 ResourceTableType* type, ResourceEntry* entry) { 37 if (pkg->id.value() == id.package_id()) { 38 if (!type->id || type->id.value() == id.type_id()) { 39 type->id = id.type_id(); 40 41 if (!entry->id || entry->id.value() == id.entry_id()) { 42 entry->id = id.entry_id(); 43 return true; 44 } 45 } 46 } 47 48 const ResourceId existing_id(pkg->id.value(), type->id ? type->id.value() : 0, 49 entry->id ? entry->id.value() : 0); 50 diag->Error(DiagMessage() << "can't assign ID " << id << " to resource " 51 << name << " with conflicting ID " << existing_id); 52 return false; 53 } 54 55 bool IdAssigner::Consume(IAaptContext* context, ResourceTable* table) { 56 std::map<ResourceId, ResourceName> assigned_ids; 57 58 for (auto& package : table->packages) { 59 CHECK(bool(package->id)) << "packages must have manually assigned IDs"; 60 61 for (auto& type : package->types) { 62 for (auto& entry : type->entries) { 63 const ResourceName name(package->name, type->type, entry->name); 64 65 if (assigned_id_map_) { 66 // Assign the pre-assigned stable ID meant for this resource. 67 const auto iter = assigned_id_map_->find(name); 68 if (iter != assigned_id_map_->end()) { 69 const ResourceId assigned_id = iter->second; 70 const bool result = 71 AssignId(context->GetDiagnostics(), assigned_id, name, 72 package.get(), type.get(), entry.get()); 73 if (!result) { 74 return false; 75 } 76 } 77 } 78 79 if (package->id && type->id && entry->id) { 80 // If the ID is set for this resource, then reserve it. 81 ResourceId resource_id(package->id.value(), type->id.value(), 82 entry->id.value()); 83 auto result = assigned_ids.insert({resource_id, name}); 84 const ResourceName& existing_name = result.first->second; 85 if (!result.second) { 86 context->GetDiagnostics()->Error( 87 DiagMessage() << "resource " << name << " has same ID " 88 << resource_id << " as " << existing_name); 89 return false; 90 } 91 } 92 } 93 } 94 } 95 96 if (assigned_id_map_) { 97 // Reserve all the IDs mentioned in the stable ID map. That way we won't 98 // assign 99 // IDs that were listed in the map if they don't exist in the table. 100 for (const auto& stable_id_entry : *assigned_id_map_) { 101 const ResourceName& pre_assigned_name = stable_id_entry.first; 102 const ResourceId& pre_assigned_id = stable_id_entry.second; 103 auto result = assigned_ids.insert({pre_assigned_id, pre_assigned_name}); 104 const ResourceName& existing_name = result.first->second; 105 if (!result.second && existing_name != pre_assigned_name) { 106 context->GetDiagnostics()->Error( 107 DiagMessage() << "stable ID " << pre_assigned_id << " for resource " 108 << pre_assigned_name 109 << " is already taken by resource " << existing_name); 110 return false; 111 } 112 } 113 } 114 115 // Assign any resources without IDs the next available ID. Gaps will be filled 116 // if possible, 117 // unless those IDs have been reserved. 118 119 const auto assigned_ids_iter_end = assigned_ids.end(); 120 for (auto& package : table->packages) { 121 CHECK(bool(package->id)) << "packages must have manually assigned IDs"; 122 123 // Build a half filled ResourceId object, which will be used to find the 124 // closest matching 125 // reserved ID in the assignedId map. From that point the next available 126 // type ID can be 127 // found. 128 ResourceId resource_id(package->id.value(), 0, 0); 129 uint8_t next_expected_type_id = 1; 130 131 // Find the closest matching ResourceId that is <= the one with only the 132 // package set. 133 auto next_type_iter = assigned_ids.lower_bound(resource_id); 134 for (auto& type : package->types) { 135 if (!type->id) { 136 // We need to assign a type ID. Iterate over the reserved IDs until we 137 // find 138 // some type ID that is a distance of 2 greater than the last one we've 139 // seen. 140 // That means there is an available type ID between these reserved IDs. 141 while (next_type_iter != assigned_ids_iter_end) { 142 if (next_type_iter->first.package_id() != package->id.value()) { 143 break; 144 } 145 146 const uint8_t type_id = next_type_iter->first.type_id(); 147 if (type_id > next_expected_type_id) { 148 // There is a gap in the type IDs, so use the missing one. 149 type->id = next_expected_type_id++; 150 break; 151 } 152 153 // Set our expectation to be the next type ID after the reserved one 154 // we 155 // just saw. 156 next_expected_type_id = type_id + 1; 157 158 // Move to the next reserved ID. 159 ++next_type_iter; 160 } 161 162 if (!type->id) { 163 // We must have hit the end of the reserved IDs and not found a gap. 164 // That means the next ID is available. 165 type->id = next_expected_type_id++; 166 } 167 } 168 169 resource_id = ResourceId(package->id.value(), type->id.value(), 0); 170 uint16_t next_expected_entry_id = 0; 171 172 // Find the closest matching ResourceId that is <= the one with only the 173 // package 174 // and type set. 175 auto next_entry_iter = assigned_ids.lower_bound(resource_id); 176 for (auto& entry : type->entries) { 177 if (!entry->id) { 178 // We need to assign an entry ID. Iterate over the reserved IDs until 179 // we find 180 // some entry ID that is a distance of 2 greater than the last one 181 // we've seen. 182 // That means there is an available entry ID between these reserved 183 // IDs. 184 while (next_entry_iter != assigned_ids_iter_end) { 185 if (next_entry_iter->first.package_id() != package->id.value() || 186 next_entry_iter->first.type_id() != type->id.value()) { 187 break; 188 } 189 190 const uint16_t entry_id = next_entry_iter->first.entry_id(); 191 if (entry_id > next_expected_entry_id) { 192 // There is a gap in the entry IDs, so use the missing one. 193 entry->id = next_expected_entry_id++; 194 break; 195 } 196 197 // Set our expectation to be the next type ID after the reserved one 198 // we 199 // just saw. 200 next_expected_entry_id = entry_id + 1; 201 202 // Move to the next reserved entry ID. 203 ++next_entry_iter; 204 } 205 206 if (!entry->id) { 207 // We must have hit the end of the reserved IDs and not found a gap. 208 // That means the next ID is available. 209 entry->id = next_expected_entry_id++; 210 } 211 } 212 } 213 } 214 } 215 return true; 216 } 217 218 } // namespace aapt 219