| /* | |
| * Copyright (C) 2014 The Android Open Source Project | |
| * | |
| * Licensed under the Apache License, Version 2.0 (the "License"); | |
| * you may not use this file except in compliance with the License. | |
| * You may obtain a copy of the License at | |
| * | |
| * http://www.apache.org/licenses/LICENSE-2.0 | |
| * | |
| * Unless required by applicable law or agreed to in writing, software | |
| * distributed under the License is distributed on an "AS IS" BASIS, | |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
| * See the License for the specific language governing permissions and | |
| * limitations under the License. | |
| */ | |
| #include "AaptAssets.h" | |
| #include "ApkBuilder.h" | |
| using namespace android; | |
| ApkBuilder::ApkBuilder(const sp<WeakResourceFilter>& configFilter) | |
| : mConfigFilter(configFilter) | |
| , mDefaultFilter(new AndResourceFilter()) { | |
| // Add the default split, which is present for all APKs. | |
| mDefaultFilter->addFilter(mConfigFilter); | |
| mSplits.add(new ApkSplit(std::set<ConfigDescription>(), mDefaultFilter, true)); | |
| } | |
| status_t ApkBuilder::createSplitForConfigs(const std::set<ConfigDescription>& configs) { | |
| const size_t N = mSplits.size(); | |
| for (size_t i = 0; i < N; i++) { | |
| const std::set<ConfigDescription>& splitConfigs = mSplits[i]->getConfigs(); | |
| std::set<ConfigDescription>::const_iterator iter = configs.begin(); | |
| for (; iter != configs.end(); iter++) { | |
| if (splitConfigs.count(*iter) > 0) { | |
| // Can't have overlapping configurations. | |
| fprintf(stderr, "ERROR: Split configuration '%s' is already defined " | |
| "in another split.\n", iter->toString().c_str()); | |
| return ALREADY_EXISTS; | |
| } | |
| } | |
| } | |
| sp<StrongResourceFilter> splitFilter = new StrongResourceFilter(configs); | |
| // Add the inverse filter of this split filter to the base apk filter so it will | |
| // omit resources that belong in this split. | |
| mDefaultFilter->addFilter(new InverseResourceFilter(splitFilter)); | |
| // Now add the apk-wide config filter to our split filter. | |
| sp<AndResourceFilter> filter = new AndResourceFilter(); | |
| filter->addFilter(splitFilter); | |
| filter->addFilter(mConfigFilter); | |
| mSplits.add(new ApkSplit(configs, filter)); | |
| return NO_ERROR; | |
| } | |
| status_t ApkBuilder::addEntry(const String8& path, const sp<AaptFile>& file) { | |
| const size_t N = mSplits.size(); | |
| for (size_t i = 0; i < N; i++) { | |
| if (mSplits[i]->matches(file)) { | |
| return mSplits.editItemAt(i)->addEntry(path, file); | |
| } | |
| } | |
| // Entry can be dropped if it doesn't match any split. This will only happen | |
| // if the enry doesn't mConfigFilter. | |
| return NO_ERROR; | |
| } | |
| void ApkBuilder::print() const { | |
| fprintf(stderr, "APK Builder\n"); | |
| fprintf(stderr, "-----------\n"); | |
| const size_t N = mSplits.size(); | |
| for (size_t i = 0; i < N; i++) { | |
| mSplits[i]->print(); | |
| fprintf(stderr, "\n"); | |
| } | |
| } | |
| ApkSplit::ApkSplit(const std::set<ConfigDescription>& configs, const sp<ResourceFilter>& filter, bool isBase) | |
| : mConfigs(configs), mFilter(filter), mIsBase(isBase) { | |
| std::set<ConfigDescription>::const_iterator iter = configs.begin(); | |
| for (; iter != configs.end(); iter++) { | |
| if (mName.size() > 0) { | |
| mName.append(","); | |
| mDirName.append("_"); | |
| mPackageSafeName.append("."); | |
| } | |
| String8 configStr = iter->toString(); | |
| String8 packageConfigStr(configStr); | |
| size_t len = packageConfigStr.length(); | |
| if (len > 0) { | |
| char* buf = packageConfigStr.lockBuffer(len); | |
| for (char* end = buf + len; buf < end; ++buf) { | |
| if (*buf == '-') { | |
| *buf = '_'; | |
| } | |
| } | |
| packageConfigStr.unlockBuffer(len); | |
| } | |
| mName.append(configStr); | |
| mDirName.append(configStr); | |
| mPackageSafeName.append(packageConfigStr); | |
| } | |
| } | |
| status_t ApkSplit::addEntry(const String8& path, const sp<AaptFile>& file) { | |
| if (!mFiles.insert(OutputEntry(path, file)).second) { | |
| // Duplicate file. | |
| return ALREADY_EXISTS; | |
| } | |
| return NO_ERROR; | |
| } | |
| void ApkSplit::print() const { | |
| fprintf(stderr, "APK Split '%s'\n", mName.c_str()); | |
| std::set<OutputEntry>::const_iterator iter = mFiles.begin(); | |
| for (; iter != mFiles.end(); iter++) { | |
| fprintf(stderr, " %s (%s)\n", iter->getPath().c_str(), iter->getFile()->getSourceFile().c_str()); | |
| } | |
| } |