Home | History | Annotate | Download | only in link
      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