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