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
17ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski#include "link/Linkers.h"
18ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski
19ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski#include <algorithm>
20ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski
21ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski#include "android-base/logging.h"
22ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski
231ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski#include "ConfigDescription.h"
241ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski#include "ResourceTable.h"
251ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski#include "SdkConstants.h"
261ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski#include "ValueVisitor.h"
271ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski
281ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinskinamespace aapt {
291ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski
30c744ae8aca97edfb2422598ea620e8219449fa9bAdam Lesinskibool ShouldGenerateVersionedResource(const ResourceEntry* entry, const ConfigDescription& config,
31c744ae8aca97edfb2422598ea620e8219449fa9bAdam Lesinski                                     const ApiVersion sdk_version_to_generate) {
32c744ae8aca97edfb2422598ea620e8219449fa9bAdam Lesinski  // We assume the caller is trying to generate a version greater than the current configuration.
33ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  CHECK(sdk_version_to_generate > config.sdkVersion);
34c744ae8aca97edfb2422598ea620e8219449fa9bAdam Lesinski  return sdk_version_to_generate < FindNextApiVersionForConfig(entry, config);
35c744ae8aca97edfb2422598ea620e8219449fa9bAdam Lesinski}
36cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski
37c744ae8aca97edfb2422598ea620e8219449fa9bAdam LesinskiApiVersion FindNextApiVersionForConfig(const ResourceEntry* entry,
38c744ae8aca97edfb2422598ea620e8219449fa9bAdam Lesinski                                       const ConfigDescription& config) {
39ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  const auto end_iter = entry->values.end();
40cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  auto iter = entry->values.begin();
41ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  for (; iter != end_iter; ++iter) {
42cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    if ((*iter)->config == config) {
43cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      break;
44e4bb9eb5af5b0899dc0921d5580220b20e15bd5aAdam Lesinski    }
45cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  }
46cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski
47cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  // The source config came from this list, so it should be here.
48ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  CHECK(iter != entry->values.end());
49cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  ++iter;
50cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski
51c744ae8aca97edfb2422598ea620e8219449fa9bAdam Lesinski  // The next configuration either only varies in sdkVersion, or it is completely different
52c744ae8aca97edfb2422598ea620e8219449fa9bAdam Lesinski  // and therefore incompatible. If it is incompatible, we must generate the versioned resource.
53cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski
54c744ae8aca97edfb2422598ea620e8219449fa9bAdam Lesinski  // NOTE: The ordering of configurations takes sdkVersion as higher precedence than other
55cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  // qualifiers, so we need to iterate through the entire list to be sure there
56cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  // are no higher sdk level versions of this resource.
57ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  ConfigDescription temp_config(config);
58ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  for (; iter != end_iter; ++iter) {
59ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski    temp_config.sdkVersion = (*iter)->config.sdkVersion;
60ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski    if (temp_config == (*iter)->config) {
61c744ae8aca97edfb2422598ea620e8219449fa9bAdam Lesinski      // The two configs are the same, return the sdkVersion.
62c744ae8aca97edfb2422598ea620e8219449fa9bAdam Lesinski      return (*iter)->config.sdkVersion;
631ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski    }
64cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  }
651ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski
66c744ae8aca97edfb2422598ea620e8219449fa9bAdam Lesinski  // Didn't find another config with a different sdk version, so return the highest possible value.
67c744ae8aca97edfb2422598ea620e8219449fa9bAdam Lesinski  return std::numeric_limits<ApiVersion>::max();
681ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski}
691ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski
70ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinskibool AutoVersioner::Consume(IAaptContext* context, ResourceTable* table) {
71cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  for (auto& package : table->packages) {
72cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    for (auto& type : package->types) {
73cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      if (type->type != ResourceType::kStyle) {
74cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski        continue;
75cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      }
76cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski
77cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      for (auto& entry : type->entries) {
78cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski        for (size_t i = 0; i < entry->values.size(); i++) {
79ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski          ResourceConfigValue* config_value = entry->values[i].get();
80ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski          if (config_value->config.sdkVersion >= SDK_LOLLIPOP_MR1) {
81cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski            // If this configuration is only used on L-MR1 then we don't need
82cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski            // to do anything since we use private attributes since that
83cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski            // version.
84cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski            continue;
85cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski          }
86cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski
87ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski          if (Style* style = ValueCast<Style>(config_value->value.get())) {
88c744ae8aca97edfb2422598ea620e8219449fa9bAdam Lesinski            Maybe<ApiVersion> min_sdk_stripped;
89cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski            std::vector<Style::Entry> stripped;
90cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski
91cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski            auto iter = style->entries.begin();
92cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski            while (iter != style->entries.end()) {
93ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski              CHECK(bool(iter->key.id)) << "IDs must be assigned and linked";
94cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski
95cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski              // Find the SDK level that is higher than the configuration
96cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski              // allows.
97c744ae8aca97edfb2422598ea620e8219449fa9bAdam Lesinski              const ApiVersion sdk_level = FindAttributeSdkLevel(iter->key.id.value());
98c744ae8aca97edfb2422598ea620e8219449fa9bAdam Lesinski              if (sdk_level > std::max<ApiVersion>(config_value->config.sdkVersion, 1)) {
99cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski                // Record that we are about to strip this.
100cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski                stripped.emplace_back(std::move(*iter));
101cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski
102cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski                // We use the smallest SDK level to generate the new style.
103ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski                if (min_sdk_stripped) {
104c744ae8aca97edfb2422598ea620e8219449fa9bAdam Lesinski                  min_sdk_stripped = std::min(min_sdk_stripped.value(), sdk_level);
105cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski                } else {
106ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski                  min_sdk_stripped = sdk_level;
107cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski                }
108cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski
109cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski                // Erase this from this style.
110cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski                iter = style->entries.erase(iter);
1111ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski                continue;
112cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski              }
113cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski              ++iter;
1141ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski            }
1151ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski
116ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski            if (min_sdk_stripped && !stripped.empty()) {
117cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski              // We found attributes from a higher SDK level. Check that
118cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski              // there is no other defined resource for the version we want to
119cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski              // generate.
120ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski              if (ShouldGenerateVersionedResource(entry.get(),
121ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski                                                  config_value->config,
122ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski                                                  min_sdk_stripped.value())) {
123cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski                // Let's create a new Style for this versioned resource.
124ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski                ConfigDescription new_config(config_value->config);
125c744ae8aca97edfb2422598ea620e8219449fa9bAdam Lesinski                new_config.sdkVersion = static_cast<uint16_t>(min_sdk_stripped.value());
126cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski
127c744ae8aca97edfb2422598ea620e8219449fa9bAdam Lesinski                std::unique_ptr<Style> new_style(style->Clone(&table->string_pool));
128ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski                new_style->SetComment(style->GetComment());
129ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski                new_style->SetSource(style->GetSource());
130cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski
131cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski                // Move the previously stripped attributes into this style.
132ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski                new_style->entries.insert(
133ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski                    new_style->entries.end(),
134cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski                    std::make_move_iterator(stripped.begin()),
135cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski                    std::make_move_iterator(stripped.end()));
136cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski
137cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski                // Insert the new Resource into the correct place.
138c744ae8aca97edfb2422598ea620e8219449fa9bAdam Lesinski                entry->FindOrCreateValue(new_config, {})->value = std::move(new_style);
139cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski              }
1401ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski            }
141cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski          }
1421ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski        }
143cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      }
1441ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski    }
145cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  }
146cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  return true;
1471ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski}
1481ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski
149cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski}  // namespace aapt
150