Loading include/androidfw/ResourceTypes.h +1 −1 Original line number Diff line number Diff line Loading @@ -1779,7 +1779,7 @@ public: const DynamicRefTable* getDynamicRefTableForCookie(int32_t cookie) const; // Return the configurations (ResTable_config) that we know about void getConfigurations(Vector<ResTable_config>* configs) const; void getConfigurations(Vector<ResTable_config>* configs, bool ignoreMipmap=false) const; void getLocales(Vector<String8>* locales) const; Loading libs/androidfw/ResourceTypes.cpp +7 −1 Original line number Diff line number Diff line Loading @@ -5487,7 +5487,7 @@ const DynamicRefTable* ResTable::getDynamicRefTableForCookie(int32_t cookie) con return NULL; } void ResTable::getConfigurations(Vector<ResTable_config>* configs) const void ResTable::getConfigurations(Vector<ResTable_config>* configs, bool ignoreMipmap) const { const size_t packageCount = mPackageGroups.size(); for (size_t i = 0; i < packageCount; i++) { Loading @@ -5498,6 +5498,12 @@ void ResTable::getConfigurations(Vector<ResTable_config>* configs) const const size_t numTypes = typeList.size(); for (size_t k = 0; k < numTypes; k++) { const Type* type = typeList[k]; const ResStringPool& typeStrings = type->package->typeStrings; if (ignoreMipmap && typeStrings.string8ObjectAt( type->typeSpec->id - 1) == "mipmap") { continue; } const size_t numConfigs = type->configs.size(); for (size_t m = 0; m < numConfigs; m++) { const ResTable_type* config = type->configs[m]; Loading tools/split-select/Android.mk +3 −1 Original line number Diff line number Diff line Loading @@ -29,12 +29,14 @@ sources := \ Grouper.cpp \ Rule.cpp \ RuleGenerator.cpp \ SplitDescription.cpp SplitDescription.cpp \ SplitSelector.cpp testSources := \ Grouper_test.cpp \ Rule_test.cpp \ RuleGenerator_test.cpp \ SplitSelector_test.cpp \ TestRules.cpp cIncludes := \ Loading tools/split-select/Main.cpp +146 −87 Original line number Diff line number Diff line Loading @@ -23,6 +23,7 @@ #include "Rule.h" #include "RuleGenerator.h" #include "SplitDescription.h" #include "SplitSelector.h" #include <androidfw/AssetManager.h> #include <androidfw/ResourceTypes.h> Loading @@ -36,12 +37,13 @@ namespace split { static void usage() { fprintf(stderr, "split-select --help\n" "split-select --target <config> --split <path/to/apk> [--split <path/to/apk> [...]]\n" "split-select --generate --split <path/to/apk> [--split <path/to/apk> [...]]\n" "split-select --target <config> --base <path/to/apk> [--split <path/to/apk> [...]]\n" "split-select --generate --base <path/to/apk> [--split <path/to/apk> [...]]\n" "\n" " --help Displays more information about this program.\n" " --target <config> Performs the Split APK selection on the given configuration.\n" " --generate Generates the logic for selecting the Split APK, in JSON format.\n" " --base <path/to/apk> Specifies the base APK, from which all Split APKs must be based off.\n" " --split <path/to/apk> Includes a Split APK in the selection process.\n" "\n" " Where <config> is an extended AAPT resource qualifier of the form\n" Loading @@ -61,92 +63,33 @@ static void help() { " via JSON.\n"); } class SplitSelector { public: SplitSelector(); SplitSelector(const Vector<SplitDescription>& splits); Vector<SplitDescription> getBestSplits(const SplitDescription& target) const; template <typename RuleGenerator> KeyedVector<SplitDescription, sp<Rule> > getRules() const; private: Vector<SortedVector<SplitDescription> > mGroups; }; SplitSelector::SplitSelector() { } SplitSelector::SplitSelector(const Vector<SplitDescription>& splits) : mGroups(groupByMutualExclusivity(splits)) { } static void selectBestFromGroup(const SortedVector<SplitDescription>& splits, const SplitDescription& target, Vector<SplitDescription>& splitsOut) { SplitDescription bestSplit; bool isSet = false; const size_t splitCount = splits.size(); for (size_t j = 0; j < splitCount; j++) { const SplitDescription& thisSplit = splits[j]; if (!thisSplit.match(target)) { continue; } if (!isSet || thisSplit.isBetterThan(bestSplit, target)) { isSet = true; bestSplit = thisSplit; } } if (isSet) { splitsOut.add(bestSplit); } } Vector<SplitDescription> SplitSelector::getBestSplits(const SplitDescription& target) const { Vector<SplitDescription> bestSplits; const size_t groupCount = mGroups.size(); for (size_t i = 0; i < groupCount; i++) { selectBestFromGroup(mGroups[i], target, bestSplits); } return bestSplits; } template <typename RuleGenerator> KeyedVector<SplitDescription, sp<Rule> > SplitSelector::getRules() const { KeyedVector<SplitDescription, sp<Rule> > rules; const size_t groupCount = mGroups.size(); for (size_t i = 0; i < groupCount; i++) { const SortedVector<SplitDescription>& splits = mGroups[i]; const size_t splitCount = splits.size(); for (size_t j = 0; j < splitCount; j++) { sp<Rule> rule = Rule::simplify(RuleGenerator::generate(splits, j)); if (rule != NULL) { rules.add(splits[j], rule); } } } return rules; } Vector<SplitDescription> select(const SplitDescription& target, const Vector<SplitDescription>& splits) { const SplitSelector selector(splits); return selector.getBestSplits(target); } void generate(const KeyedVector<String8, Vector<SplitDescription> >& splits) { void generate(const KeyedVector<String8, Vector<SplitDescription> >& splits, const String8& base) { Vector<SplitDescription> allSplits; const size_t apkSplitCount = splits.size(); for (size_t i = 0; i < apkSplitCount; i++) { allSplits.appendVector(splits[i]); } const SplitSelector selector(allSplits); KeyedVector<SplitDescription, sp<Rule> > rules(selector.getRules<RuleGenerator>()); KeyedVector<SplitDescription, sp<Rule> > rules(selector.getRules()); bool first = true; fprintf(stdout, "[\n"); for (size_t i = 0; i < apkSplitCount; i++) { if (splits.keyAt(i) == base) { // Skip the base. continue; } if (!first) { fprintf(stdout, ",\n"); } first = false; sp<Rule> masterRule = new Rule(); masterRule->op = Rule::OR_SUBRULES; const Vector<SplitDescription>& splitDescriptions = splits[i]; Loading @@ -155,12 +98,11 @@ void generate(const KeyedVector<String8, Vector<SplitDescription> >& splits) { masterRule->subrules.add(rules.valueFor(splitDescriptions[j])); } masterRule = Rule::simplify(masterRule); fprintf(stdout, " {\n \"path\": \"%s\",\n \"rules\": %s\n }%s\n", fprintf(stdout, " {\n \"path\": \"%s\",\n \"rules\": %s\n }", splits.keyAt(i).string(), masterRule->toJson(2).string(), i < apkSplitCount - 1 ? "," : ""); masterRule->toJson(2).string()); } fprintf(stdout, "]\n"); fprintf(stdout, "\n]\n"); } static void removeRuntimeQualifiers(ConfigDescription* outConfig) { Loading @@ -171,6 +113,95 @@ static void removeRuntimeQualifiers(ConfigDescription* outConfig) { outConfig->uiMode &= ResTable_config::UI_MODE_NIGHT_ANY; } struct AppInfo { int versionCode; int minSdkVersion; bool multiArch; }; static bool getAppInfo(const String8& path, AppInfo& outInfo) { memset(&outInfo, 0, sizeof(outInfo)); AssetManager assetManager; int32_t cookie = 0; if (!assetManager.addAssetPath(path, &cookie)) { return false; } Asset* asset = assetManager.openNonAsset(cookie, "AndroidManifest.xml", Asset::ACCESS_BUFFER); if (asset == NULL) { return false; } ResXMLTree xml; if (xml.setTo(asset->getBuffer(true), asset->getLength(), false) != NO_ERROR) { delete asset; return false; } const String16 kAndroidNamespace("http://schemas.android.com/apk/res/android"); const String16 kManifestTag("manifest"); const String16 kApplicationTag("application"); const String16 kUsesSdkTag("uses-sdk"); const String16 kVersionCodeAttr("versionCode"); const String16 kMultiArchAttr("multiArch"); const String16 kMinSdkVersionAttr("minSdkVersion"); ResXMLParser::event_code_t event; while ((event = xml.next()) != ResXMLParser::BAD_DOCUMENT && event != ResXMLParser::END_DOCUMENT) { if (event != ResXMLParser::START_TAG) { continue; } size_t len; const char16_t* name = xml.getElementName(&len); String16 name16(name, len); if (name16 == kManifestTag) { ssize_t idx = xml.indexOfAttribute( kAndroidNamespace.string(), kAndroidNamespace.size(), kVersionCodeAttr.string(), kVersionCodeAttr.size()); if (idx >= 0) { outInfo.versionCode = xml.getAttributeData(idx); } } else if (name16 == kApplicationTag) { ssize_t idx = xml.indexOfAttribute( kAndroidNamespace.string(), kAndroidNamespace.size(), kMultiArchAttr.string(), kMultiArchAttr.size()); if (idx >= 0) { outInfo.multiArch = xml.getAttributeData(idx) != 0; } } else if (name16 == kUsesSdkTag) { ssize_t idx = xml.indexOfAttribute( kAndroidNamespace.string(), kAndroidNamespace.size(), kMinSdkVersionAttr.string(), kMinSdkVersionAttr.size()); if (idx >= 0) { uint16_t type = xml.getAttributeDataType(idx); if (type >= Res_value::TYPE_FIRST_INT && type <= Res_value::TYPE_LAST_INT) { outInfo.minSdkVersion = xml.getAttributeData(idx); } else if (type == Res_value::TYPE_STRING) { String8 minSdk8(xml.getStrings().string8ObjectAt(idx)); char* endPtr; int minSdk = strtol(minSdk8.string(), &endPtr, 10); if (endPtr != minSdk8.string() + minSdk8.size()) { fprintf(stderr, "warning: failed to parse android:minSdkVersion '%s'\n", minSdk8.string()); } else { outInfo.minSdkVersion = minSdk; } } else { fprintf(stderr, "warning: unrecognized value for android:minSdkVersion.\n"); } } } } delete asset; return true; } static Vector<SplitDescription> extractSplitDescriptionsFromApk(const String8& path) { AssetManager assetManager; Vector<SplitDescription> splits; Loading @@ -182,7 +213,7 @@ static Vector<SplitDescription> extractSplitDescriptionsFromApk(const String8& p const ResTable& res = assetManager.getResources(false); if (res.getError() == NO_ERROR) { Vector<ResTable_config> configs; res.getConfigurations(&configs); res.getConfigurations(&configs, true); const size_t configCount = configs.size(); for (size_t i = 0; i < configCount; i++) { splits.add(); Loading Loading @@ -214,13 +245,14 @@ static int main(int argc, char** argv) { bool generateFlag = false; String8 targetConfigStr; Vector<String8> splitApkPaths; String8 baseApkPath; while (argc > 0) { const String8 arg(*argv); if (arg == "--target") { argc--; argv++; if (argc < 1) { fprintf(stderr, "Missing parameter for --split.\n"); fprintf(stderr, "error: missing parameter for --target.\n"); usage(); return 1; } Loading @@ -229,18 +261,33 @@ static int main(int argc, char** argv) { argc--; argv++; if (argc < 1) { fprintf(stderr, "Missing parameter for --split.\n"); fprintf(stderr, "error: missing parameter for --split.\n"); usage(); return 1; } splitApkPaths.add(String8(*argv)); } else if (arg == "--base") { argc--; argv++; if (argc < 1) { fprintf(stderr, "error: missing parameter for --base.\n"); usage(); return 1; } if (baseApkPath.size() > 0) { fprintf(stderr, "error: multiple --base flags not allowed.\n"); usage(); return 1; } baseApkPath.setTo(*argv); } else if (arg == "--generate") { generateFlag = true; } else if (arg == "--help") { help(); return 0; } else { fprintf(stderr, "Unknown argument '%s'\n", arg.string()); fprintf(stderr, "error: unknown argument '%s'.\n", arg.string()); usage(); return 1; } Loading @@ -253,15 +300,23 @@ static int main(int argc, char** argv) { return 1; } if (splitApkPaths.size() == 0) { if (baseApkPath.size() == 0) { fprintf(stderr, "error: missing --base argument.\n"); usage(); return 1; } // Find out some details about the base APK. AppInfo baseAppInfo; if (!getAppInfo(baseApkPath, baseAppInfo)) { fprintf(stderr, "error: unable to read base APK: '%s'.\n", baseApkPath.string()); return 1; } SplitDescription targetSplit; if (!generateFlag) { if (!SplitDescription::parse(targetConfigStr, &targetSplit)) { fprintf(stderr, "Invalid --target config: '%s'\n", fprintf(stderr, "error: invalid --target config: '%s'.\n", targetConfigStr.string()); usage(); return 1; Loading @@ -272,6 +327,8 @@ static int main(int argc, char** argv) { removeRuntimeQualifiers(&targetSplit.config); } splitApkPaths.add(baseApkPath); KeyedVector<String8, Vector<SplitDescription> > apkPathSplitMap; KeyedVector<SplitDescription, String8> splitApkPathMap; Vector<SplitDescription> splitConfigs; Loading @@ -279,7 +336,7 @@ static int main(int argc, char** argv) { for (size_t i = 0; i < splitCount; i++) { Vector<SplitDescription> splits = extractSplitDescriptionsFromApk(splitApkPaths[i]); if (splits.isEmpty()) { fprintf(stderr, "Invalid --split path: '%s'. No splits found.\n", fprintf(stderr, "error: invalid --split path: '%s'. No splits found.\n", splitApkPaths[i].string()); usage(); return 1; Loading @@ -302,10 +359,12 @@ static int main(int argc, char** argv) { const size_t matchingSplitApkPathCount = matchingSplitPaths.size(); for (size_t i = 0; i < matchingSplitApkPathCount; i++) { fprintf(stderr, "%s\n", matchingSplitPaths[i].string()); if (matchingSplitPaths[i] != baseApkPath) { fprintf(stdout, "%s\n", matchingSplitPaths[i].string()); } } } else { generate(apkPathSplitMap); generate(apkPathSplitMap, baseApkPath); } return 0; } Loading tools/split-select/SplitSelector.cpp 0 → 100644 +85 −0 Original line number Diff line number Diff line /* * 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 <utils/KeyedVector.h> #include <utils/SortedVector.h> #include <utils/Vector.h> #include "Grouper.h" #include "Rule.h" #include "RuleGenerator.h" #include "SplitSelector.h" namespace split { using namespace android; SplitSelector::SplitSelector() { } SplitSelector::SplitSelector(const Vector<SplitDescription>& splits) : mGroups(groupByMutualExclusivity(splits)) { } static void selectBestFromGroup(const SortedVector<SplitDescription>& splits, const SplitDescription& target, Vector<SplitDescription>& splitsOut) { SplitDescription bestSplit; bool isSet = false; const size_t splitCount = splits.size(); for (size_t j = 0; j < splitCount; j++) { const SplitDescription& thisSplit = splits[j]; if (!thisSplit.match(target)) { continue; } if (!isSet || thisSplit.isBetterThan(bestSplit, target)) { isSet = true; bestSplit = thisSplit; } } if (isSet) { splitsOut.add(bestSplit); } } Vector<SplitDescription> SplitSelector::getBestSplits(const SplitDescription& target) const { Vector<SplitDescription> bestSplits; const size_t groupCount = mGroups.size(); for (size_t i = 0; i < groupCount; i++) { selectBestFromGroup(mGroups[i], target, bestSplits); } return bestSplits; } KeyedVector<SplitDescription, sp<Rule> > SplitSelector::getRules() const { KeyedVector<SplitDescription, sp<Rule> > rules; const size_t groupCount = mGroups.size(); for (size_t i = 0; i < groupCount; i++) { const SortedVector<SplitDescription>& splits = mGroups[i]; const size_t splitCount = splits.size(); for (size_t j = 0; j < splitCount; j++) { sp<Rule> rule = Rule::simplify(RuleGenerator::generate(splits, j)); if (rule != NULL) { rules.add(splits[j], rule); } } } return rules; } } // namespace split Loading
include/androidfw/ResourceTypes.h +1 −1 Original line number Diff line number Diff line Loading @@ -1779,7 +1779,7 @@ public: const DynamicRefTable* getDynamicRefTableForCookie(int32_t cookie) const; // Return the configurations (ResTable_config) that we know about void getConfigurations(Vector<ResTable_config>* configs) const; void getConfigurations(Vector<ResTable_config>* configs, bool ignoreMipmap=false) const; void getLocales(Vector<String8>* locales) const; Loading
libs/androidfw/ResourceTypes.cpp +7 −1 Original line number Diff line number Diff line Loading @@ -5487,7 +5487,7 @@ const DynamicRefTable* ResTable::getDynamicRefTableForCookie(int32_t cookie) con return NULL; } void ResTable::getConfigurations(Vector<ResTable_config>* configs) const void ResTable::getConfigurations(Vector<ResTable_config>* configs, bool ignoreMipmap) const { const size_t packageCount = mPackageGroups.size(); for (size_t i = 0; i < packageCount; i++) { Loading @@ -5498,6 +5498,12 @@ void ResTable::getConfigurations(Vector<ResTable_config>* configs) const const size_t numTypes = typeList.size(); for (size_t k = 0; k < numTypes; k++) { const Type* type = typeList[k]; const ResStringPool& typeStrings = type->package->typeStrings; if (ignoreMipmap && typeStrings.string8ObjectAt( type->typeSpec->id - 1) == "mipmap") { continue; } const size_t numConfigs = type->configs.size(); for (size_t m = 0; m < numConfigs; m++) { const ResTable_type* config = type->configs[m]; Loading
tools/split-select/Android.mk +3 −1 Original line number Diff line number Diff line Loading @@ -29,12 +29,14 @@ sources := \ Grouper.cpp \ Rule.cpp \ RuleGenerator.cpp \ SplitDescription.cpp SplitDescription.cpp \ SplitSelector.cpp testSources := \ Grouper_test.cpp \ Rule_test.cpp \ RuleGenerator_test.cpp \ SplitSelector_test.cpp \ TestRules.cpp cIncludes := \ Loading
tools/split-select/Main.cpp +146 −87 Original line number Diff line number Diff line Loading @@ -23,6 +23,7 @@ #include "Rule.h" #include "RuleGenerator.h" #include "SplitDescription.h" #include "SplitSelector.h" #include <androidfw/AssetManager.h> #include <androidfw/ResourceTypes.h> Loading @@ -36,12 +37,13 @@ namespace split { static void usage() { fprintf(stderr, "split-select --help\n" "split-select --target <config> --split <path/to/apk> [--split <path/to/apk> [...]]\n" "split-select --generate --split <path/to/apk> [--split <path/to/apk> [...]]\n" "split-select --target <config> --base <path/to/apk> [--split <path/to/apk> [...]]\n" "split-select --generate --base <path/to/apk> [--split <path/to/apk> [...]]\n" "\n" " --help Displays more information about this program.\n" " --target <config> Performs the Split APK selection on the given configuration.\n" " --generate Generates the logic for selecting the Split APK, in JSON format.\n" " --base <path/to/apk> Specifies the base APK, from which all Split APKs must be based off.\n" " --split <path/to/apk> Includes a Split APK in the selection process.\n" "\n" " Where <config> is an extended AAPT resource qualifier of the form\n" Loading @@ -61,92 +63,33 @@ static void help() { " via JSON.\n"); } class SplitSelector { public: SplitSelector(); SplitSelector(const Vector<SplitDescription>& splits); Vector<SplitDescription> getBestSplits(const SplitDescription& target) const; template <typename RuleGenerator> KeyedVector<SplitDescription, sp<Rule> > getRules() const; private: Vector<SortedVector<SplitDescription> > mGroups; }; SplitSelector::SplitSelector() { } SplitSelector::SplitSelector(const Vector<SplitDescription>& splits) : mGroups(groupByMutualExclusivity(splits)) { } static void selectBestFromGroup(const SortedVector<SplitDescription>& splits, const SplitDescription& target, Vector<SplitDescription>& splitsOut) { SplitDescription bestSplit; bool isSet = false; const size_t splitCount = splits.size(); for (size_t j = 0; j < splitCount; j++) { const SplitDescription& thisSplit = splits[j]; if (!thisSplit.match(target)) { continue; } if (!isSet || thisSplit.isBetterThan(bestSplit, target)) { isSet = true; bestSplit = thisSplit; } } if (isSet) { splitsOut.add(bestSplit); } } Vector<SplitDescription> SplitSelector::getBestSplits(const SplitDescription& target) const { Vector<SplitDescription> bestSplits; const size_t groupCount = mGroups.size(); for (size_t i = 0; i < groupCount; i++) { selectBestFromGroup(mGroups[i], target, bestSplits); } return bestSplits; } template <typename RuleGenerator> KeyedVector<SplitDescription, sp<Rule> > SplitSelector::getRules() const { KeyedVector<SplitDescription, sp<Rule> > rules; const size_t groupCount = mGroups.size(); for (size_t i = 0; i < groupCount; i++) { const SortedVector<SplitDescription>& splits = mGroups[i]; const size_t splitCount = splits.size(); for (size_t j = 0; j < splitCount; j++) { sp<Rule> rule = Rule::simplify(RuleGenerator::generate(splits, j)); if (rule != NULL) { rules.add(splits[j], rule); } } } return rules; } Vector<SplitDescription> select(const SplitDescription& target, const Vector<SplitDescription>& splits) { const SplitSelector selector(splits); return selector.getBestSplits(target); } void generate(const KeyedVector<String8, Vector<SplitDescription> >& splits) { void generate(const KeyedVector<String8, Vector<SplitDescription> >& splits, const String8& base) { Vector<SplitDescription> allSplits; const size_t apkSplitCount = splits.size(); for (size_t i = 0; i < apkSplitCount; i++) { allSplits.appendVector(splits[i]); } const SplitSelector selector(allSplits); KeyedVector<SplitDescription, sp<Rule> > rules(selector.getRules<RuleGenerator>()); KeyedVector<SplitDescription, sp<Rule> > rules(selector.getRules()); bool first = true; fprintf(stdout, "[\n"); for (size_t i = 0; i < apkSplitCount; i++) { if (splits.keyAt(i) == base) { // Skip the base. continue; } if (!first) { fprintf(stdout, ",\n"); } first = false; sp<Rule> masterRule = new Rule(); masterRule->op = Rule::OR_SUBRULES; const Vector<SplitDescription>& splitDescriptions = splits[i]; Loading @@ -155,12 +98,11 @@ void generate(const KeyedVector<String8, Vector<SplitDescription> >& splits) { masterRule->subrules.add(rules.valueFor(splitDescriptions[j])); } masterRule = Rule::simplify(masterRule); fprintf(stdout, " {\n \"path\": \"%s\",\n \"rules\": %s\n }%s\n", fprintf(stdout, " {\n \"path\": \"%s\",\n \"rules\": %s\n }", splits.keyAt(i).string(), masterRule->toJson(2).string(), i < apkSplitCount - 1 ? "," : ""); masterRule->toJson(2).string()); } fprintf(stdout, "]\n"); fprintf(stdout, "\n]\n"); } static void removeRuntimeQualifiers(ConfigDescription* outConfig) { Loading @@ -171,6 +113,95 @@ static void removeRuntimeQualifiers(ConfigDescription* outConfig) { outConfig->uiMode &= ResTable_config::UI_MODE_NIGHT_ANY; } struct AppInfo { int versionCode; int minSdkVersion; bool multiArch; }; static bool getAppInfo(const String8& path, AppInfo& outInfo) { memset(&outInfo, 0, sizeof(outInfo)); AssetManager assetManager; int32_t cookie = 0; if (!assetManager.addAssetPath(path, &cookie)) { return false; } Asset* asset = assetManager.openNonAsset(cookie, "AndroidManifest.xml", Asset::ACCESS_BUFFER); if (asset == NULL) { return false; } ResXMLTree xml; if (xml.setTo(asset->getBuffer(true), asset->getLength(), false) != NO_ERROR) { delete asset; return false; } const String16 kAndroidNamespace("http://schemas.android.com/apk/res/android"); const String16 kManifestTag("manifest"); const String16 kApplicationTag("application"); const String16 kUsesSdkTag("uses-sdk"); const String16 kVersionCodeAttr("versionCode"); const String16 kMultiArchAttr("multiArch"); const String16 kMinSdkVersionAttr("minSdkVersion"); ResXMLParser::event_code_t event; while ((event = xml.next()) != ResXMLParser::BAD_DOCUMENT && event != ResXMLParser::END_DOCUMENT) { if (event != ResXMLParser::START_TAG) { continue; } size_t len; const char16_t* name = xml.getElementName(&len); String16 name16(name, len); if (name16 == kManifestTag) { ssize_t idx = xml.indexOfAttribute( kAndroidNamespace.string(), kAndroidNamespace.size(), kVersionCodeAttr.string(), kVersionCodeAttr.size()); if (idx >= 0) { outInfo.versionCode = xml.getAttributeData(idx); } } else if (name16 == kApplicationTag) { ssize_t idx = xml.indexOfAttribute( kAndroidNamespace.string(), kAndroidNamespace.size(), kMultiArchAttr.string(), kMultiArchAttr.size()); if (idx >= 0) { outInfo.multiArch = xml.getAttributeData(idx) != 0; } } else if (name16 == kUsesSdkTag) { ssize_t idx = xml.indexOfAttribute( kAndroidNamespace.string(), kAndroidNamespace.size(), kMinSdkVersionAttr.string(), kMinSdkVersionAttr.size()); if (idx >= 0) { uint16_t type = xml.getAttributeDataType(idx); if (type >= Res_value::TYPE_FIRST_INT && type <= Res_value::TYPE_LAST_INT) { outInfo.minSdkVersion = xml.getAttributeData(idx); } else if (type == Res_value::TYPE_STRING) { String8 minSdk8(xml.getStrings().string8ObjectAt(idx)); char* endPtr; int minSdk = strtol(minSdk8.string(), &endPtr, 10); if (endPtr != minSdk8.string() + minSdk8.size()) { fprintf(stderr, "warning: failed to parse android:minSdkVersion '%s'\n", minSdk8.string()); } else { outInfo.minSdkVersion = minSdk; } } else { fprintf(stderr, "warning: unrecognized value for android:minSdkVersion.\n"); } } } } delete asset; return true; } static Vector<SplitDescription> extractSplitDescriptionsFromApk(const String8& path) { AssetManager assetManager; Vector<SplitDescription> splits; Loading @@ -182,7 +213,7 @@ static Vector<SplitDescription> extractSplitDescriptionsFromApk(const String8& p const ResTable& res = assetManager.getResources(false); if (res.getError() == NO_ERROR) { Vector<ResTable_config> configs; res.getConfigurations(&configs); res.getConfigurations(&configs, true); const size_t configCount = configs.size(); for (size_t i = 0; i < configCount; i++) { splits.add(); Loading Loading @@ -214,13 +245,14 @@ static int main(int argc, char** argv) { bool generateFlag = false; String8 targetConfigStr; Vector<String8> splitApkPaths; String8 baseApkPath; while (argc > 0) { const String8 arg(*argv); if (arg == "--target") { argc--; argv++; if (argc < 1) { fprintf(stderr, "Missing parameter for --split.\n"); fprintf(stderr, "error: missing parameter for --target.\n"); usage(); return 1; } Loading @@ -229,18 +261,33 @@ static int main(int argc, char** argv) { argc--; argv++; if (argc < 1) { fprintf(stderr, "Missing parameter for --split.\n"); fprintf(stderr, "error: missing parameter for --split.\n"); usage(); return 1; } splitApkPaths.add(String8(*argv)); } else if (arg == "--base") { argc--; argv++; if (argc < 1) { fprintf(stderr, "error: missing parameter for --base.\n"); usage(); return 1; } if (baseApkPath.size() > 0) { fprintf(stderr, "error: multiple --base flags not allowed.\n"); usage(); return 1; } baseApkPath.setTo(*argv); } else if (arg == "--generate") { generateFlag = true; } else if (arg == "--help") { help(); return 0; } else { fprintf(stderr, "Unknown argument '%s'\n", arg.string()); fprintf(stderr, "error: unknown argument '%s'.\n", arg.string()); usage(); return 1; } Loading @@ -253,15 +300,23 @@ static int main(int argc, char** argv) { return 1; } if (splitApkPaths.size() == 0) { if (baseApkPath.size() == 0) { fprintf(stderr, "error: missing --base argument.\n"); usage(); return 1; } // Find out some details about the base APK. AppInfo baseAppInfo; if (!getAppInfo(baseApkPath, baseAppInfo)) { fprintf(stderr, "error: unable to read base APK: '%s'.\n", baseApkPath.string()); return 1; } SplitDescription targetSplit; if (!generateFlag) { if (!SplitDescription::parse(targetConfigStr, &targetSplit)) { fprintf(stderr, "Invalid --target config: '%s'\n", fprintf(stderr, "error: invalid --target config: '%s'.\n", targetConfigStr.string()); usage(); return 1; Loading @@ -272,6 +327,8 @@ static int main(int argc, char** argv) { removeRuntimeQualifiers(&targetSplit.config); } splitApkPaths.add(baseApkPath); KeyedVector<String8, Vector<SplitDescription> > apkPathSplitMap; KeyedVector<SplitDescription, String8> splitApkPathMap; Vector<SplitDescription> splitConfigs; Loading @@ -279,7 +336,7 @@ static int main(int argc, char** argv) { for (size_t i = 0; i < splitCount; i++) { Vector<SplitDescription> splits = extractSplitDescriptionsFromApk(splitApkPaths[i]); if (splits.isEmpty()) { fprintf(stderr, "Invalid --split path: '%s'. No splits found.\n", fprintf(stderr, "error: invalid --split path: '%s'. No splits found.\n", splitApkPaths[i].string()); usage(); return 1; Loading @@ -302,10 +359,12 @@ static int main(int argc, char** argv) { const size_t matchingSplitApkPathCount = matchingSplitPaths.size(); for (size_t i = 0; i < matchingSplitApkPathCount; i++) { fprintf(stderr, "%s\n", matchingSplitPaths[i].string()); if (matchingSplitPaths[i] != baseApkPath) { fprintf(stdout, "%s\n", matchingSplitPaths[i].string()); } } } else { generate(apkPathSplitMap); generate(apkPathSplitMap, baseApkPath); } return 0; } Loading
tools/split-select/SplitSelector.cpp 0 → 100644 +85 −0 Original line number Diff line number Diff line /* * 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 <utils/KeyedVector.h> #include <utils/SortedVector.h> #include <utils/Vector.h> #include "Grouper.h" #include "Rule.h" #include "RuleGenerator.h" #include "SplitSelector.h" namespace split { using namespace android; SplitSelector::SplitSelector() { } SplitSelector::SplitSelector(const Vector<SplitDescription>& splits) : mGroups(groupByMutualExclusivity(splits)) { } static void selectBestFromGroup(const SortedVector<SplitDescription>& splits, const SplitDescription& target, Vector<SplitDescription>& splitsOut) { SplitDescription bestSplit; bool isSet = false; const size_t splitCount = splits.size(); for (size_t j = 0; j < splitCount; j++) { const SplitDescription& thisSplit = splits[j]; if (!thisSplit.match(target)) { continue; } if (!isSet || thisSplit.isBetterThan(bestSplit, target)) { isSet = true; bestSplit = thisSplit; } } if (isSet) { splitsOut.add(bestSplit); } } Vector<SplitDescription> SplitSelector::getBestSplits(const SplitDescription& target) const { Vector<SplitDescription> bestSplits; const size_t groupCount = mGroups.size(); for (size_t i = 0; i < groupCount; i++) { selectBestFromGroup(mGroups[i], target, bestSplits); } return bestSplits; } KeyedVector<SplitDescription, sp<Rule> > SplitSelector::getRules() const { KeyedVector<SplitDescription, sp<Rule> > rules; const size_t groupCount = mGroups.size(); for (size_t i = 0; i < groupCount; i++) { const SortedVector<SplitDescription>& splits = mGroups[i]; const size_t splitCount = splits.size(); for (size_t j = 0; j < splitCount; j++) { sp<Rule> rule = Rule::simplify(RuleGenerator::generate(splits, j)); if (rule != NULL) { rules.add(splits[j], rule); } } } return rules; } } // namespace split