1 /*
2 * Copyright (C) 2014 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 "AaptAssets.h"
18 #include "ApkBuilder.h"
19
20 using namespace android;
21
ApkBuilder(const sp<WeakResourceFilter> & configFilter)22 ApkBuilder::ApkBuilder(const sp<WeakResourceFilter>& configFilter)
23 : mConfigFilter(configFilter)
24 , mDefaultFilter(new AndResourceFilter()) {
25 // Add the default split, which is present for all APKs.
26 mDefaultFilter->addFilter(mConfigFilter);
27 mSplits.add(new ApkSplit(std::set<ConfigDescription>(), mDefaultFilter, true));
28 }
29
createSplitForConfigs(const std::set<ConfigDescription> & configs)30 status_t ApkBuilder::createSplitForConfigs(const std::set<ConfigDescription>& configs) {
31 const size_t N = mSplits.size();
32 for (size_t i = 0; i < N; i++) {
33 const std::set<ConfigDescription>& splitConfigs = mSplits[i]->getConfigs();
34 std::set<ConfigDescription>::const_iterator iter = configs.begin();
35 for (; iter != configs.end(); iter++) {
36 if (splitConfigs.count(*iter) > 0) {
37 // Can't have overlapping configurations.
38 fprintf(stderr, "ERROR: Split configuration '%s' is already defined "
39 "in another split.\n", iter->toString().string());
40 return ALREADY_EXISTS;
41 }
42 }
43 }
44
45 sp<StrongResourceFilter> splitFilter = new StrongResourceFilter(configs);
46
47 // Add the inverse filter of this split filter to the base apk filter so it will
48 // omit resources that belong in this split.
49 mDefaultFilter->addFilter(new InverseResourceFilter(splitFilter));
50
51 // Now add the apk-wide config filter to our split filter.
52 sp<AndResourceFilter> filter = new AndResourceFilter();
53 filter->addFilter(splitFilter);
54 filter->addFilter(mConfigFilter);
55 mSplits.add(new ApkSplit(configs, filter));
56 return NO_ERROR;
57 }
58
addEntry(const String8 & path,const sp<AaptFile> & file)59 status_t ApkBuilder::addEntry(const String8& path, const sp<AaptFile>& file) {
60 const size_t N = mSplits.size();
61 for (size_t i = 0; i < N; i++) {
62 if (mSplits[i]->matches(file)) {
63 return mSplits.editItemAt(i)->addEntry(path, file);
64 }
65 }
66 // Entry can be dropped if it doesn't match any split. This will only happen
67 // if the enry doesn't mConfigFilter.
68 return NO_ERROR;
69 }
70
print() const71 void ApkBuilder::print() const {
72 fprintf(stderr, "APK Builder\n");
73 fprintf(stderr, "-----------\n");
74 const size_t N = mSplits.size();
75 for (size_t i = 0; i < N; i++) {
76 mSplits[i]->print();
77 fprintf(stderr, "\n");
78 }
79 }
80
ApkSplit(const std::set<ConfigDescription> & configs,const sp<ResourceFilter> & filter,bool isBase)81 ApkSplit::ApkSplit(const std::set<ConfigDescription>& configs, const sp<ResourceFilter>& filter, bool isBase)
82 : mConfigs(configs), mFilter(filter), mIsBase(isBase) {
83 std::set<ConfigDescription>::const_iterator iter = configs.begin();
84 for (; iter != configs.end(); iter++) {
85 if (mName.size() > 0) {
86 mName.append(",");
87 mDirName.append("_");
88 mPackageSafeName.append(".");
89 }
90
91 String8 configStr = iter->toString();
92 String8 packageConfigStr(configStr);
93 size_t len = packageConfigStr.length();
94 if (len > 0) {
95 char* buf = packageConfigStr.lockBuffer(len);
96 for (char* end = buf + len; buf < end; ++buf) {
97 if (*buf == '-') {
98 *buf = '_';
99 }
100 }
101 packageConfigStr.unlockBuffer(len);
102 }
103 mName.append(configStr);
104 mDirName.append(configStr);
105 mPackageSafeName.append(packageConfigStr);
106 }
107 }
108
addEntry(const String8 & path,const sp<AaptFile> & file)109 status_t ApkSplit::addEntry(const String8& path, const sp<AaptFile>& file) {
110 if (!mFiles.insert(OutputEntry(path, file)).second) {
111 // Duplicate file.
112 return ALREADY_EXISTS;
113 }
114 return NO_ERROR;
115 }
116
print() const117 void ApkSplit::print() const {
118 fprintf(stderr, "APK Split '%s'\n", mName.string());
119
120 std::set<OutputEntry>::const_iterator iter = mFiles.begin();
121 for (; iter != mFiles.end(); iter++) {
122 fprintf(stderr, " %s (%s)\n", iter->getPath().string(), iter->getFile()->getSourceFile().string());
123 }
124 }
125