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 #include "androidfw/Locale.h" 18 19 #include "link/NoDefaultResourceRemover.h" 20 21 #include <algorithm> 22 23 #include "ResourceTable.h" 24 25 using android::ConfigDescription; 26 27 namespace aapt { 28 29 static bool KeepResource(const std::unique_ptr<ResourceEntry>& entry, int minSdk) { 30 if (entry->visibility.level == Visibility::Level::kPublic) { 31 // Removing a public API without the developer knowing is bad, so just leave this here for now. 32 return true; 33 } 34 35 if (entry->HasDefaultValue()) { 36 // There is a default value, no removal needed. 37 return true; 38 } 39 40 // There is no default value defined, check if removal is required. 41 bool defaultRequired = false; 42 for (const auto& config_value : entry->values) { 43 const int config = ConfigDescription::DefaultConfig().diff(config_value->config); 44 // If a resource defines a value for a locale-only configuration, the default configuration is 45 // required. 46 if (config == ConfigDescription::CONFIG_LOCALE) { 47 defaultRequired = true; 48 } 49 // If a resource defines a version-only config, the config value can be used as a default if 50 // the version is at most the minimum sdk version 51 else if (config == ConfigDescription::CONFIG_VERSION 52 && config_value->config.sdkVersion <= minSdk) { 53 return true; 54 } 55 // If a resource defines a value for a density only configuration, then that value could be used 56 // as a default and the entry should not be removed 57 else if (config == ConfigDescription::CONFIG_DENSITY 58 || (config == (ConfigDescription::CONFIG_DENSITY | ConfigDescription::CONFIG_VERSION) 59 && config_value->config.sdkVersion <= minSdk)) { 60 return true; 61 } 62 } 63 64 return !defaultRequired; 65 } 66 67 bool NoDefaultResourceRemover::Consume(IAaptContext* context, ResourceTable* table) { 68 for (auto& pkg : table->packages) { 69 for (auto& type : pkg->types) { 70 // Gather the entries without defaults that must be removed 71 const int minSdk = context->GetMinSdkVersion(); 72 const auto end_iter = type->entries.end(); 73 const auto remove_iter = std::stable_partition(type->entries.begin(), end_iter, 74 [&minSdk](const std::unique_ptr<ResourceEntry>& entry) -> bool { 75 return KeepResource(entry, minSdk); 76 }); 77 78 for (auto iter = remove_iter; iter != end_iter; ++iter) { 79 const ResourceName name(pkg->name, type->type, (*iter)->name); 80 IDiagnostics* diag = context->GetDiagnostics(); 81 diag->Warn(DiagMessage() << "removing resource " << name 82 << " without required default value"); 83 if (context->IsVerbose()) { 84 diag->Note(DiagMessage() << " did you forget to remove all definitions?"); 85 for (const auto& config_value : (*iter)->values) { 86 if (config_value->value != nullptr) { 87 diag->Note(DiagMessage(config_value->value->GetSource()) << "defined here"); 88 } 89 } 90 } 91 } 92 93 type->entries.erase(remove_iter, end_iter); 94 } 95 } 96 return true; 97 } 98 99 } // namespace aapt 100