140e8eefbedcafc51948945647d746daaee092f16Adam Lesinski/*
240e8eefbedcafc51948945647d746daaee092f16Adam Lesinski * Copyright (C) 2014 The Android Open Source Project
340e8eefbedcafc51948945647d746daaee092f16Adam Lesinski *
440e8eefbedcafc51948945647d746daaee092f16Adam Lesinski * Licensed under the Apache License, Version 2.0 (the "License");
540e8eefbedcafc51948945647d746daaee092f16Adam Lesinski * you may not use this file except in compliance with the License.
640e8eefbedcafc51948945647d746daaee092f16Adam Lesinski * You may obtain a copy of the License at
740e8eefbedcafc51948945647d746daaee092f16Adam Lesinski *
840e8eefbedcafc51948945647d746daaee092f16Adam Lesinski *      http://www.apache.org/licenses/LICENSE-2.0
940e8eefbedcafc51948945647d746daaee092f16Adam Lesinski *
1040e8eefbedcafc51948945647d746daaee092f16Adam Lesinski * Unless required by applicable law or agreed to in writing, software
1140e8eefbedcafc51948945647d746daaee092f16Adam Lesinski * distributed under the License is distributed on an "AS IS" BASIS,
1240e8eefbedcafc51948945647d746daaee092f16Adam Lesinski * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
1340e8eefbedcafc51948945647d746daaee092f16Adam Lesinski * See the License for the specific language governing permissions and
1440e8eefbedcafc51948945647d746daaee092f16Adam Lesinski * limitations under the License.
1540e8eefbedcafc51948945647d746daaee092f16Adam Lesinski */
1640e8eefbedcafc51948945647d746daaee092f16Adam Lesinski
1740e8eefbedcafc51948945647d746daaee092f16Adam Lesinski#include <algorithm>
1840e8eefbedcafc51948945647d746daaee092f16Adam Lesinski#include <cstdio>
1940e8eefbedcafc51948945647d746daaee092f16Adam Lesinski
2040e8eefbedcafc51948945647d746daaee092f16Adam Lesinski#include "aapt/AaptUtil.h"
2140e8eefbedcafc51948945647d746daaee092f16Adam Lesinski
2240e8eefbedcafc51948945647d746daaee092f16Adam Lesinski#include "Grouper.h"
2340e8eefbedcafc51948945647d746daaee092f16Adam Lesinski#include "Rule.h"
2440e8eefbedcafc51948945647d746daaee092f16Adam Lesinski#include "RuleGenerator.h"
2540e8eefbedcafc51948945647d746daaee092f16Adam Lesinski#include "SplitDescription.h"
2642eea270a0a2bc54f454312817c41ac357e3a884Adam Lesinski#include "SplitSelector.h"
2740e8eefbedcafc51948945647d746daaee092f16Adam Lesinski
2840e8eefbedcafc51948945647d746daaee092f16Adam Lesinski#include <androidfw/AssetManager.h>
2940e8eefbedcafc51948945647d746daaee092f16Adam Lesinski#include <androidfw/ResourceTypes.h>
3040e8eefbedcafc51948945647d746daaee092f16Adam Lesinski#include <utils/KeyedVector.h>
3140e8eefbedcafc51948945647d746daaee092f16Adam Lesinski#include <utils/Vector.h>
3240e8eefbedcafc51948945647d746daaee092f16Adam Lesinski
3340e8eefbedcafc51948945647d746daaee092f16Adam Lesinskiusing namespace android;
3440e8eefbedcafc51948945647d746daaee092f16Adam Lesinski
3540e8eefbedcafc51948945647d746daaee092f16Adam Lesinskinamespace split {
3640e8eefbedcafc51948945647d746daaee092f16Adam Lesinski
3740e8eefbedcafc51948945647d746daaee092f16Adam Lesinskistatic void usage() {
3840e8eefbedcafc51948945647d746daaee092f16Adam Lesinski    fprintf(stderr,
3940e8eefbedcafc51948945647d746daaee092f16Adam Lesinski            "split-select --help\n"
4042eea270a0a2bc54f454312817c41ac357e3a884Adam Lesinski            "split-select --target <config> --base <path/to/apk> [--split <path/to/apk> [...]]\n"
4142eea270a0a2bc54f454312817c41ac357e3a884Adam Lesinski            "split-select --generate --base <path/to/apk> [--split <path/to/apk> [...]]\n"
4240e8eefbedcafc51948945647d746daaee092f16Adam Lesinski            "\n"
4340e8eefbedcafc51948945647d746daaee092f16Adam Lesinski            "  --help                   Displays more information about this program.\n"
4440e8eefbedcafc51948945647d746daaee092f16Adam Lesinski            "  --target <config>        Performs the Split APK selection on the given configuration.\n"
4540e8eefbedcafc51948945647d746daaee092f16Adam Lesinski            "  --generate               Generates the logic for selecting the Split APK, in JSON format.\n"
4642eea270a0a2bc54f454312817c41ac357e3a884Adam Lesinski            "  --base <path/to/apk>     Specifies the base APK, from which all Split APKs must be based off.\n"
4740e8eefbedcafc51948945647d746daaee092f16Adam Lesinski            "  --split <path/to/apk>    Includes a Split APK in the selection process.\n"
4840e8eefbedcafc51948945647d746daaee092f16Adam Lesinski            "\n"
4940e8eefbedcafc51948945647d746daaee092f16Adam Lesinski            "  Where <config> is an extended AAPT resource qualifier of the form\n"
5040e8eefbedcafc51948945647d746daaee092f16Adam Lesinski            "  'resource-qualifiers:extended-qualifiers', where 'resource-qualifiers' is an AAPT resource\n"
5140e8eefbedcafc51948945647d746daaee092f16Adam Lesinski            "  qualifier (ex: en-rUS-sw600dp-xhdpi), and 'extended-qualifiers' is an ordered list of one\n"
5240e8eefbedcafc51948945647d746daaee092f16Adam Lesinski            "  qualifier (or none) from each category:\n"
5340e8eefbedcafc51948945647d746daaee092f16Adam Lesinski            "    Architecture: armeabi, armeabi-v7a, arm64-v8a, x86, x86_64, mips\n");
5440e8eefbedcafc51948945647d746daaee092f16Adam Lesinski}
5540e8eefbedcafc51948945647d746daaee092f16Adam Lesinski
5640e8eefbedcafc51948945647d746daaee092f16Adam Lesinskistatic void help() {
5740e8eefbedcafc51948945647d746daaee092f16Adam Lesinski    usage();
5840e8eefbedcafc51948945647d746daaee092f16Adam Lesinski    fprintf(stderr, "\n"
5940e8eefbedcafc51948945647d746daaee092f16Adam Lesinski            "  Generates the logic for selecting a Split APK given some target Android device configuration.\n"
6040e8eefbedcafc51948945647d746daaee092f16Adam Lesinski            "  Using the flag --generate will emit a JSON encoded tree of rules that must be satisfied in order\n"
6140e8eefbedcafc51948945647d746daaee092f16Adam Lesinski            "  to install the given Split APK. Using the flag --target along with the device configuration\n"
6240e8eefbedcafc51948945647d746daaee092f16Adam Lesinski            "  will emit the set of Split APKs to install, following the same logic that would have been emitted\n"
6340e8eefbedcafc51948945647d746daaee092f16Adam Lesinski            "  via JSON.\n");
6440e8eefbedcafc51948945647d746daaee092f16Adam Lesinski}
6540e8eefbedcafc51948945647d746daaee092f16Adam Lesinski
6640e8eefbedcafc51948945647d746daaee092f16Adam LesinskiVector<SplitDescription> select(const SplitDescription& target, const Vector<SplitDescription>& splits) {
6740e8eefbedcafc51948945647d746daaee092f16Adam Lesinski    const SplitSelector selector(splits);
6840e8eefbedcafc51948945647d746daaee092f16Adam Lesinski    return selector.getBestSplits(target);
6940e8eefbedcafc51948945647d746daaee092f16Adam Lesinski}
7040e8eefbedcafc51948945647d746daaee092f16Adam Lesinski
7142eea270a0a2bc54f454312817c41ac357e3a884Adam Lesinskivoid generate(const KeyedVector<String8, Vector<SplitDescription> >& splits, const String8& base) {
7240e8eefbedcafc51948945647d746daaee092f16Adam Lesinski    Vector<SplitDescription> allSplits;
7340e8eefbedcafc51948945647d746daaee092f16Adam Lesinski    const size_t apkSplitCount = splits.size();
7440e8eefbedcafc51948945647d746daaee092f16Adam Lesinski    for (size_t i = 0; i < apkSplitCount; i++) {
7540e8eefbedcafc51948945647d746daaee092f16Adam Lesinski        allSplits.appendVector(splits[i]);
7640e8eefbedcafc51948945647d746daaee092f16Adam Lesinski    }
7740e8eefbedcafc51948945647d746daaee092f16Adam Lesinski    const SplitSelector selector(allSplits);
7842eea270a0a2bc54f454312817c41ac357e3a884Adam Lesinski    KeyedVector<SplitDescription, sp<Rule> > rules(selector.getRules());
7940e8eefbedcafc51948945647d746daaee092f16Adam Lesinski
8042eea270a0a2bc54f454312817c41ac357e3a884Adam Lesinski    bool first = true;
8140e8eefbedcafc51948945647d746daaee092f16Adam Lesinski    fprintf(stdout, "[\n");
8240e8eefbedcafc51948945647d746daaee092f16Adam Lesinski    for (size_t i = 0; i < apkSplitCount; i++) {
8342eea270a0a2bc54f454312817c41ac357e3a884Adam Lesinski        if (splits.keyAt(i) == base) {
8442eea270a0a2bc54f454312817c41ac357e3a884Adam Lesinski            // Skip the base.
8542eea270a0a2bc54f454312817c41ac357e3a884Adam Lesinski            continue;
8642eea270a0a2bc54f454312817c41ac357e3a884Adam Lesinski        }
8742eea270a0a2bc54f454312817c41ac357e3a884Adam Lesinski
8842eea270a0a2bc54f454312817c41ac357e3a884Adam Lesinski        if (!first) {
8942eea270a0a2bc54f454312817c41ac357e3a884Adam Lesinski            fprintf(stdout, ",\n");
9042eea270a0a2bc54f454312817c41ac357e3a884Adam Lesinski        }
9142eea270a0a2bc54f454312817c41ac357e3a884Adam Lesinski        first = false;
9242eea270a0a2bc54f454312817c41ac357e3a884Adam Lesinski
9340e8eefbedcafc51948945647d746daaee092f16Adam Lesinski        sp<Rule> masterRule = new Rule();
9440e8eefbedcafc51948945647d746daaee092f16Adam Lesinski        masterRule->op = Rule::OR_SUBRULES;
9540e8eefbedcafc51948945647d746daaee092f16Adam Lesinski        const Vector<SplitDescription>& splitDescriptions = splits[i];
9640e8eefbedcafc51948945647d746daaee092f16Adam Lesinski        const size_t splitDescriptionCount = splitDescriptions.size();
9740e8eefbedcafc51948945647d746daaee092f16Adam Lesinski        for (size_t j = 0; j < splitDescriptionCount; j++) {
9840e8eefbedcafc51948945647d746daaee092f16Adam Lesinski            masterRule->subrules.add(rules.valueFor(splitDescriptions[j]));
9940e8eefbedcafc51948945647d746daaee092f16Adam Lesinski        }
10040e8eefbedcafc51948945647d746daaee092f16Adam Lesinski        masterRule = Rule::simplify(masterRule);
10142eea270a0a2bc54f454312817c41ac357e3a884Adam Lesinski        fprintf(stdout, "  {\n    \"path\": \"%s\",\n    \"rules\": %s\n  }",
10240e8eefbedcafc51948945647d746daaee092f16Adam Lesinski                splits.keyAt(i).string(),
10342eea270a0a2bc54f454312817c41ac357e3a884Adam Lesinski                masterRule->toJson(2).string());
10440e8eefbedcafc51948945647d746daaee092f16Adam Lesinski    }
10542eea270a0a2bc54f454312817c41ac357e3a884Adam Lesinski    fprintf(stdout, "\n]\n");
10640e8eefbedcafc51948945647d746daaee092f16Adam Lesinski}
10740e8eefbedcafc51948945647d746daaee092f16Adam Lesinski
10840e8eefbedcafc51948945647d746daaee092f16Adam Lesinskistatic void removeRuntimeQualifiers(ConfigDescription* outConfig) {
10940e8eefbedcafc51948945647d746daaee092f16Adam Lesinski    outConfig->imsi = 0;
11040e8eefbedcafc51948945647d746daaee092f16Adam Lesinski    outConfig->orientation = ResTable_config::ORIENTATION_ANY;
11140e8eefbedcafc51948945647d746daaee092f16Adam Lesinski    outConfig->screenWidth = ResTable_config::SCREENWIDTH_ANY;
11240e8eefbedcafc51948945647d746daaee092f16Adam Lesinski    outConfig->screenHeight = ResTable_config::SCREENHEIGHT_ANY;
11340e8eefbedcafc51948945647d746daaee092f16Adam Lesinski    outConfig->uiMode &= ResTable_config::UI_MODE_NIGHT_ANY;
11440e8eefbedcafc51948945647d746daaee092f16Adam Lesinski}
11540e8eefbedcafc51948945647d746daaee092f16Adam Lesinski
11642eea270a0a2bc54f454312817c41ac357e3a884Adam Lesinskistruct AppInfo {
11742eea270a0a2bc54f454312817c41ac357e3a884Adam Lesinski    int versionCode;
11842eea270a0a2bc54f454312817c41ac357e3a884Adam Lesinski    int minSdkVersion;
11942eea270a0a2bc54f454312817c41ac357e3a884Adam Lesinski    bool multiArch;
12042eea270a0a2bc54f454312817c41ac357e3a884Adam Lesinski};
12142eea270a0a2bc54f454312817c41ac357e3a884Adam Lesinski
12242eea270a0a2bc54f454312817c41ac357e3a884Adam Lesinskistatic bool getAppInfo(const String8& path, AppInfo& outInfo) {
12342eea270a0a2bc54f454312817c41ac357e3a884Adam Lesinski    memset(&outInfo, 0, sizeof(outInfo));
12442eea270a0a2bc54f454312817c41ac357e3a884Adam Lesinski
12542eea270a0a2bc54f454312817c41ac357e3a884Adam Lesinski    AssetManager assetManager;
12642eea270a0a2bc54f454312817c41ac357e3a884Adam Lesinski    int32_t cookie = 0;
12742eea270a0a2bc54f454312817c41ac357e3a884Adam Lesinski    if (!assetManager.addAssetPath(path, &cookie)) {
12842eea270a0a2bc54f454312817c41ac357e3a884Adam Lesinski        return false;
12942eea270a0a2bc54f454312817c41ac357e3a884Adam Lesinski    }
13042eea270a0a2bc54f454312817c41ac357e3a884Adam Lesinski
13142eea270a0a2bc54f454312817c41ac357e3a884Adam Lesinski    Asset* asset = assetManager.openNonAsset(cookie, "AndroidManifest.xml", Asset::ACCESS_BUFFER);
13242eea270a0a2bc54f454312817c41ac357e3a884Adam Lesinski    if (asset == NULL) {
13342eea270a0a2bc54f454312817c41ac357e3a884Adam Lesinski        return false;
13442eea270a0a2bc54f454312817c41ac357e3a884Adam Lesinski    }
13542eea270a0a2bc54f454312817c41ac357e3a884Adam Lesinski
13642eea270a0a2bc54f454312817c41ac357e3a884Adam Lesinski    ResXMLTree xml;
13742eea270a0a2bc54f454312817c41ac357e3a884Adam Lesinski    if (xml.setTo(asset->getBuffer(true), asset->getLength(), false) != NO_ERROR) {
13842eea270a0a2bc54f454312817c41ac357e3a884Adam Lesinski        delete asset;
13942eea270a0a2bc54f454312817c41ac357e3a884Adam Lesinski        return false;
14042eea270a0a2bc54f454312817c41ac357e3a884Adam Lesinski    }
14142eea270a0a2bc54f454312817c41ac357e3a884Adam Lesinski
14242eea270a0a2bc54f454312817c41ac357e3a884Adam Lesinski    const String16 kAndroidNamespace("http://schemas.android.com/apk/res/android");
14342eea270a0a2bc54f454312817c41ac357e3a884Adam Lesinski    const String16 kManifestTag("manifest");
14442eea270a0a2bc54f454312817c41ac357e3a884Adam Lesinski    const String16 kApplicationTag("application");
14542eea270a0a2bc54f454312817c41ac357e3a884Adam Lesinski    const String16 kUsesSdkTag("uses-sdk");
14642eea270a0a2bc54f454312817c41ac357e3a884Adam Lesinski    const String16 kVersionCodeAttr("versionCode");
14742eea270a0a2bc54f454312817c41ac357e3a884Adam Lesinski    const String16 kMultiArchAttr("multiArch");
14842eea270a0a2bc54f454312817c41ac357e3a884Adam Lesinski    const String16 kMinSdkVersionAttr("minSdkVersion");
14942eea270a0a2bc54f454312817c41ac357e3a884Adam Lesinski
15042eea270a0a2bc54f454312817c41ac357e3a884Adam Lesinski    ResXMLParser::event_code_t event;
15142eea270a0a2bc54f454312817c41ac357e3a884Adam Lesinski    while ((event = xml.next()) != ResXMLParser::BAD_DOCUMENT &&
15242eea270a0a2bc54f454312817c41ac357e3a884Adam Lesinski            event != ResXMLParser::END_DOCUMENT) {
15342eea270a0a2bc54f454312817c41ac357e3a884Adam Lesinski        if (event != ResXMLParser::START_TAG) {
15442eea270a0a2bc54f454312817c41ac357e3a884Adam Lesinski            continue;
15542eea270a0a2bc54f454312817c41ac357e3a884Adam Lesinski        }
15642eea270a0a2bc54f454312817c41ac357e3a884Adam Lesinski
15742eea270a0a2bc54f454312817c41ac357e3a884Adam Lesinski        size_t len;
15842eea270a0a2bc54f454312817c41ac357e3a884Adam Lesinski        const char16_t* name = xml.getElementName(&len);
15942eea270a0a2bc54f454312817c41ac357e3a884Adam Lesinski        String16 name16(name, len);
16042eea270a0a2bc54f454312817c41ac357e3a884Adam Lesinski        if (name16 == kManifestTag) {
16142eea270a0a2bc54f454312817c41ac357e3a884Adam Lesinski            ssize_t idx = xml.indexOfAttribute(
16242eea270a0a2bc54f454312817c41ac357e3a884Adam Lesinski                    kAndroidNamespace.string(), kAndroidNamespace.size(),
16342eea270a0a2bc54f454312817c41ac357e3a884Adam Lesinski                    kVersionCodeAttr.string(), kVersionCodeAttr.size());
16442eea270a0a2bc54f454312817c41ac357e3a884Adam Lesinski            if (idx >= 0) {
16542eea270a0a2bc54f454312817c41ac357e3a884Adam Lesinski                outInfo.versionCode = xml.getAttributeData(idx);
16642eea270a0a2bc54f454312817c41ac357e3a884Adam Lesinski            }
16742eea270a0a2bc54f454312817c41ac357e3a884Adam Lesinski
16842eea270a0a2bc54f454312817c41ac357e3a884Adam Lesinski        } else if (name16 == kApplicationTag) {
16942eea270a0a2bc54f454312817c41ac357e3a884Adam Lesinski            ssize_t idx = xml.indexOfAttribute(
17042eea270a0a2bc54f454312817c41ac357e3a884Adam Lesinski                    kAndroidNamespace.string(), kAndroidNamespace.size(),
17142eea270a0a2bc54f454312817c41ac357e3a884Adam Lesinski                    kMultiArchAttr.string(), kMultiArchAttr.size());
17242eea270a0a2bc54f454312817c41ac357e3a884Adam Lesinski            if (idx >= 0) {
17342eea270a0a2bc54f454312817c41ac357e3a884Adam Lesinski                outInfo.multiArch = xml.getAttributeData(idx) != 0;
17442eea270a0a2bc54f454312817c41ac357e3a884Adam Lesinski            }
17542eea270a0a2bc54f454312817c41ac357e3a884Adam Lesinski
17642eea270a0a2bc54f454312817c41ac357e3a884Adam Lesinski        } else if (name16 == kUsesSdkTag) {
17742eea270a0a2bc54f454312817c41ac357e3a884Adam Lesinski            ssize_t idx = xml.indexOfAttribute(
17842eea270a0a2bc54f454312817c41ac357e3a884Adam Lesinski                    kAndroidNamespace.string(), kAndroidNamespace.size(),
17942eea270a0a2bc54f454312817c41ac357e3a884Adam Lesinski                    kMinSdkVersionAttr.string(), kMinSdkVersionAttr.size());
18042eea270a0a2bc54f454312817c41ac357e3a884Adam Lesinski            if (idx >= 0) {
18142eea270a0a2bc54f454312817c41ac357e3a884Adam Lesinski                uint16_t type = xml.getAttributeDataType(idx);
18242eea270a0a2bc54f454312817c41ac357e3a884Adam Lesinski                if (type >= Res_value::TYPE_FIRST_INT && type <= Res_value::TYPE_LAST_INT) {
18342eea270a0a2bc54f454312817c41ac357e3a884Adam Lesinski                    outInfo.minSdkVersion = xml.getAttributeData(idx);
18442eea270a0a2bc54f454312817c41ac357e3a884Adam Lesinski                } else if (type == Res_value::TYPE_STRING) {
18542eea270a0a2bc54f454312817c41ac357e3a884Adam Lesinski                    String8 minSdk8(xml.getStrings().string8ObjectAt(idx));
18642eea270a0a2bc54f454312817c41ac357e3a884Adam Lesinski                    char* endPtr;
18742eea270a0a2bc54f454312817c41ac357e3a884Adam Lesinski                    int minSdk = strtol(minSdk8.string(), &endPtr, 10);
18842eea270a0a2bc54f454312817c41ac357e3a884Adam Lesinski                    if (endPtr != minSdk8.string() + minSdk8.size()) {
18942eea270a0a2bc54f454312817c41ac357e3a884Adam Lesinski                        fprintf(stderr, "warning: failed to parse android:minSdkVersion '%s'\n",
19042eea270a0a2bc54f454312817c41ac357e3a884Adam Lesinski                                minSdk8.string());
19142eea270a0a2bc54f454312817c41ac357e3a884Adam Lesinski                    } else {
19242eea270a0a2bc54f454312817c41ac357e3a884Adam Lesinski                        outInfo.minSdkVersion = minSdk;
19342eea270a0a2bc54f454312817c41ac357e3a884Adam Lesinski                    }
19442eea270a0a2bc54f454312817c41ac357e3a884Adam Lesinski                } else {
19542eea270a0a2bc54f454312817c41ac357e3a884Adam Lesinski                    fprintf(stderr, "warning: unrecognized value for android:minSdkVersion.\n");
19642eea270a0a2bc54f454312817c41ac357e3a884Adam Lesinski                }
19742eea270a0a2bc54f454312817c41ac357e3a884Adam Lesinski            }
19842eea270a0a2bc54f454312817c41ac357e3a884Adam Lesinski        }
19942eea270a0a2bc54f454312817c41ac357e3a884Adam Lesinski    }
20042eea270a0a2bc54f454312817c41ac357e3a884Adam Lesinski
20142eea270a0a2bc54f454312817c41ac357e3a884Adam Lesinski    delete asset;
20242eea270a0a2bc54f454312817c41ac357e3a884Adam Lesinski    return true;
20342eea270a0a2bc54f454312817c41ac357e3a884Adam Lesinski}
20442eea270a0a2bc54f454312817c41ac357e3a884Adam Lesinski
20540e8eefbedcafc51948945647d746daaee092f16Adam Lesinskistatic Vector<SplitDescription> extractSplitDescriptionsFromApk(const String8& path) {
20640e8eefbedcafc51948945647d746daaee092f16Adam Lesinski    AssetManager assetManager;
20740e8eefbedcafc51948945647d746daaee092f16Adam Lesinski    Vector<SplitDescription> splits;
20840e8eefbedcafc51948945647d746daaee092f16Adam Lesinski    int32_t cookie = 0;
20940e8eefbedcafc51948945647d746daaee092f16Adam Lesinski    if (!assetManager.addAssetPath(path, &cookie)) {
21040e8eefbedcafc51948945647d746daaee092f16Adam Lesinski        return splits;
21140e8eefbedcafc51948945647d746daaee092f16Adam Lesinski    }
21240e8eefbedcafc51948945647d746daaee092f16Adam Lesinski
21340e8eefbedcafc51948945647d746daaee092f16Adam Lesinski    const ResTable& res = assetManager.getResources(false);
21440e8eefbedcafc51948945647d746daaee092f16Adam Lesinski    if (res.getError() == NO_ERROR) {
21540e8eefbedcafc51948945647d746daaee092f16Adam Lesinski        Vector<ResTable_config> configs;
21642eea270a0a2bc54f454312817c41ac357e3a884Adam Lesinski        res.getConfigurations(&configs, true);
21740e8eefbedcafc51948945647d746daaee092f16Adam Lesinski        const size_t configCount = configs.size();
21840e8eefbedcafc51948945647d746daaee092f16Adam Lesinski        for (size_t i = 0; i < configCount; i++) {
21940e8eefbedcafc51948945647d746daaee092f16Adam Lesinski            splits.add();
22040e8eefbedcafc51948945647d746daaee092f16Adam Lesinski            splits.editTop().config = configs[i];
22140e8eefbedcafc51948945647d746daaee092f16Adam Lesinski        }
22240e8eefbedcafc51948945647d746daaee092f16Adam Lesinski    }
22340e8eefbedcafc51948945647d746daaee092f16Adam Lesinski
22440e8eefbedcafc51948945647d746daaee092f16Adam Lesinski    AssetDir* dir = assetManager.openNonAssetDir(cookie, "lib");
22540e8eefbedcafc51948945647d746daaee092f16Adam Lesinski    if (dir != NULL) {
22640e8eefbedcafc51948945647d746daaee092f16Adam Lesinski        const size_t fileCount = dir->getFileCount();
22740e8eefbedcafc51948945647d746daaee092f16Adam Lesinski        for (size_t i = 0; i < fileCount; i++) {
22840e8eefbedcafc51948945647d746daaee092f16Adam Lesinski            splits.add();
22940e8eefbedcafc51948945647d746daaee092f16Adam Lesinski            Vector<String8> parts = AaptUtil::splitAndLowerCase(dir->getFileName(i), '-');
23040e8eefbedcafc51948945647d746daaee092f16Adam Lesinski            if (parseAbi(parts, 0, &splits.editTop()) < 0) {
23140e8eefbedcafc51948945647d746daaee092f16Adam Lesinski                fprintf(stderr, "Malformed library %s\n", dir->getFileName(i).string());
23240e8eefbedcafc51948945647d746daaee092f16Adam Lesinski                splits.pop();
23340e8eefbedcafc51948945647d746daaee092f16Adam Lesinski            }
23440e8eefbedcafc51948945647d746daaee092f16Adam Lesinski        }
23540e8eefbedcafc51948945647d746daaee092f16Adam Lesinski        delete dir;
23640e8eefbedcafc51948945647d746daaee092f16Adam Lesinski    }
23740e8eefbedcafc51948945647d746daaee092f16Adam Lesinski    return splits;
23840e8eefbedcafc51948945647d746daaee092f16Adam Lesinski}
23940e8eefbedcafc51948945647d746daaee092f16Adam Lesinski
24040e8eefbedcafc51948945647d746daaee092f16Adam Lesinskistatic int main(int argc, char** argv) {
24140e8eefbedcafc51948945647d746daaee092f16Adam Lesinski    // Skip over the first argument.
24240e8eefbedcafc51948945647d746daaee092f16Adam Lesinski    argc--;
24340e8eefbedcafc51948945647d746daaee092f16Adam Lesinski    argv++;
24440e8eefbedcafc51948945647d746daaee092f16Adam Lesinski
24540e8eefbedcafc51948945647d746daaee092f16Adam Lesinski    bool generateFlag = false;
24640e8eefbedcafc51948945647d746daaee092f16Adam Lesinski    String8 targetConfigStr;
24740e8eefbedcafc51948945647d746daaee092f16Adam Lesinski    Vector<String8> splitApkPaths;
24842eea270a0a2bc54f454312817c41ac357e3a884Adam Lesinski    String8 baseApkPath;
24940e8eefbedcafc51948945647d746daaee092f16Adam Lesinski    while (argc > 0) {
25040e8eefbedcafc51948945647d746daaee092f16Adam Lesinski        const String8 arg(*argv);
25140e8eefbedcafc51948945647d746daaee092f16Adam Lesinski        if (arg == "--target") {
25240e8eefbedcafc51948945647d746daaee092f16Adam Lesinski            argc--;
25340e8eefbedcafc51948945647d746daaee092f16Adam Lesinski            argv++;
25440e8eefbedcafc51948945647d746daaee092f16Adam Lesinski            if (argc < 1) {
25542eea270a0a2bc54f454312817c41ac357e3a884Adam Lesinski                fprintf(stderr, "error: missing parameter for --target.\n");
25640e8eefbedcafc51948945647d746daaee092f16Adam Lesinski                usage();
25740e8eefbedcafc51948945647d746daaee092f16Adam Lesinski                return 1;
25840e8eefbedcafc51948945647d746daaee092f16Adam Lesinski            }
25940e8eefbedcafc51948945647d746daaee092f16Adam Lesinski            targetConfigStr.setTo(*argv);
26040e8eefbedcafc51948945647d746daaee092f16Adam Lesinski        } else if (arg == "--split") {
26140e8eefbedcafc51948945647d746daaee092f16Adam Lesinski            argc--;
26240e8eefbedcafc51948945647d746daaee092f16Adam Lesinski            argv++;
26340e8eefbedcafc51948945647d746daaee092f16Adam Lesinski            if (argc < 1) {
26442eea270a0a2bc54f454312817c41ac357e3a884Adam Lesinski                fprintf(stderr, "error: missing parameter for --split.\n");
26540e8eefbedcafc51948945647d746daaee092f16Adam Lesinski                usage();
26640e8eefbedcafc51948945647d746daaee092f16Adam Lesinski                return 1;
26740e8eefbedcafc51948945647d746daaee092f16Adam Lesinski            }
26840e8eefbedcafc51948945647d746daaee092f16Adam Lesinski            splitApkPaths.add(String8(*argv));
26942eea270a0a2bc54f454312817c41ac357e3a884Adam Lesinski        } else if (arg == "--base") {
27042eea270a0a2bc54f454312817c41ac357e3a884Adam Lesinski            argc--;
27142eea270a0a2bc54f454312817c41ac357e3a884Adam Lesinski            argv++;
27242eea270a0a2bc54f454312817c41ac357e3a884Adam Lesinski            if (argc < 1) {
27342eea270a0a2bc54f454312817c41ac357e3a884Adam Lesinski                fprintf(stderr, "error: missing parameter for --base.\n");
27442eea270a0a2bc54f454312817c41ac357e3a884Adam Lesinski                usage();
27542eea270a0a2bc54f454312817c41ac357e3a884Adam Lesinski                return 1;
27642eea270a0a2bc54f454312817c41ac357e3a884Adam Lesinski            }
27742eea270a0a2bc54f454312817c41ac357e3a884Adam Lesinski
27842eea270a0a2bc54f454312817c41ac357e3a884Adam Lesinski            if (baseApkPath.size() > 0) {
27942eea270a0a2bc54f454312817c41ac357e3a884Adam Lesinski                fprintf(stderr, "error: multiple --base flags not allowed.\n");
28042eea270a0a2bc54f454312817c41ac357e3a884Adam Lesinski                usage();
28142eea270a0a2bc54f454312817c41ac357e3a884Adam Lesinski                return 1;
28242eea270a0a2bc54f454312817c41ac357e3a884Adam Lesinski            }
28342eea270a0a2bc54f454312817c41ac357e3a884Adam Lesinski            baseApkPath.setTo(*argv);
28440e8eefbedcafc51948945647d746daaee092f16Adam Lesinski        } else if (arg == "--generate") {
28540e8eefbedcafc51948945647d746daaee092f16Adam Lesinski            generateFlag = true;
28640e8eefbedcafc51948945647d746daaee092f16Adam Lesinski        } else if (arg == "--help") {
28740e8eefbedcafc51948945647d746daaee092f16Adam Lesinski            help();
28840e8eefbedcafc51948945647d746daaee092f16Adam Lesinski            return 0;
28940e8eefbedcafc51948945647d746daaee092f16Adam Lesinski        } else {
29042eea270a0a2bc54f454312817c41ac357e3a884Adam Lesinski            fprintf(stderr, "error: unknown argument '%s'.\n", arg.string());
29140e8eefbedcafc51948945647d746daaee092f16Adam Lesinski            usage();
29240e8eefbedcafc51948945647d746daaee092f16Adam Lesinski            return 1;
29340e8eefbedcafc51948945647d746daaee092f16Adam Lesinski        }
29440e8eefbedcafc51948945647d746daaee092f16Adam Lesinski        argc--;
29540e8eefbedcafc51948945647d746daaee092f16Adam Lesinski        argv++;
29640e8eefbedcafc51948945647d746daaee092f16Adam Lesinski    }
29740e8eefbedcafc51948945647d746daaee092f16Adam Lesinski
29840e8eefbedcafc51948945647d746daaee092f16Adam Lesinski    if (!generateFlag && targetConfigStr == "") {
29940e8eefbedcafc51948945647d746daaee092f16Adam Lesinski        usage();
30040e8eefbedcafc51948945647d746daaee092f16Adam Lesinski        return 1;
30140e8eefbedcafc51948945647d746daaee092f16Adam Lesinski    }
30240e8eefbedcafc51948945647d746daaee092f16Adam Lesinski
30342eea270a0a2bc54f454312817c41ac357e3a884Adam Lesinski    if (baseApkPath.size() == 0) {
30442eea270a0a2bc54f454312817c41ac357e3a884Adam Lesinski        fprintf(stderr, "error: missing --base argument.\n");
30540e8eefbedcafc51948945647d746daaee092f16Adam Lesinski        usage();
30640e8eefbedcafc51948945647d746daaee092f16Adam Lesinski        return 1;
30740e8eefbedcafc51948945647d746daaee092f16Adam Lesinski    }
30840e8eefbedcafc51948945647d746daaee092f16Adam Lesinski
30942eea270a0a2bc54f454312817c41ac357e3a884Adam Lesinski    // Find out some details about the base APK.
31042eea270a0a2bc54f454312817c41ac357e3a884Adam Lesinski    AppInfo baseAppInfo;
31142eea270a0a2bc54f454312817c41ac357e3a884Adam Lesinski    if (!getAppInfo(baseApkPath, baseAppInfo)) {
31242eea270a0a2bc54f454312817c41ac357e3a884Adam Lesinski        fprintf(stderr, "error: unable to read base APK: '%s'.\n", baseApkPath.string());
31342eea270a0a2bc54f454312817c41ac357e3a884Adam Lesinski        return 1;
31442eea270a0a2bc54f454312817c41ac357e3a884Adam Lesinski    }
31542eea270a0a2bc54f454312817c41ac357e3a884Adam Lesinski
31640e8eefbedcafc51948945647d746daaee092f16Adam Lesinski    SplitDescription targetSplit;
31740e8eefbedcafc51948945647d746daaee092f16Adam Lesinski    if (!generateFlag) {
31840e8eefbedcafc51948945647d746daaee092f16Adam Lesinski        if (!SplitDescription::parse(targetConfigStr, &targetSplit)) {
31942eea270a0a2bc54f454312817c41ac357e3a884Adam Lesinski            fprintf(stderr, "error: invalid --target config: '%s'.\n",
32040e8eefbedcafc51948945647d746daaee092f16Adam Lesinski                    targetConfigStr.string());
32140e8eefbedcafc51948945647d746daaee092f16Adam Lesinski            usage();
32240e8eefbedcafc51948945647d746daaee092f16Adam Lesinski            return 1;
32340e8eefbedcafc51948945647d746daaee092f16Adam Lesinski        }
32440e8eefbedcafc51948945647d746daaee092f16Adam Lesinski
32540e8eefbedcafc51948945647d746daaee092f16Adam Lesinski        // We don't want to match on things that will change at run-time
32640e8eefbedcafc51948945647d746daaee092f16Adam Lesinski        // (orientation, w/h, etc.).
32740e8eefbedcafc51948945647d746daaee092f16Adam Lesinski        removeRuntimeQualifiers(&targetSplit.config);
32840e8eefbedcafc51948945647d746daaee092f16Adam Lesinski    }
32940e8eefbedcafc51948945647d746daaee092f16Adam Lesinski
33042eea270a0a2bc54f454312817c41ac357e3a884Adam Lesinski    splitApkPaths.add(baseApkPath);
33142eea270a0a2bc54f454312817c41ac357e3a884Adam Lesinski
33240e8eefbedcafc51948945647d746daaee092f16Adam Lesinski    KeyedVector<String8, Vector<SplitDescription> > apkPathSplitMap;
33340e8eefbedcafc51948945647d746daaee092f16Adam Lesinski    KeyedVector<SplitDescription, String8> splitApkPathMap;
33440e8eefbedcafc51948945647d746daaee092f16Adam Lesinski    Vector<SplitDescription> splitConfigs;
33540e8eefbedcafc51948945647d746daaee092f16Adam Lesinski    const size_t splitCount = splitApkPaths.size();
33640e8eefbedcafc51948945647d746daaee092f16Adam Lesinski    for (size_t i = 0; i < splitCount; i++) {
33740e8eefbedcafc51948945647d746daaee092f16Adam Lesinski        Vector<SplitDescription> splits = extractSplitDescriptionsFromApk(splitApkPaths[i]);
33840e8eefbedcafc51948945647d746daaee092f16Adam Lesinski        if (splits.isEmpty()) {
33942eea270a0a2bc54f454312817c41ac357e3a884Adam Lesinski            fprintf(stderr, "error: invalid --split path: '%s'. No splits found.\n",
34040e8eefbedcafc51948945647d746daaee092f16Adam Lesinski                    splitApkPaths[i].string());
34140e8eefbedcafc51948945647d746daaee092f16Adam Lesinski            usage();
34240e8eefbedcafc51948945647d746daaee092f16Adam Lesinski            return 1;
34340e8eefbedcafc51948945647d746daaee092f16Adam Lesinski        }
34440e8eefbedcafc51948945647d746daaee092f16Adam Lesinski        apkPathSplitMap.replaceValueFor(splitApkPaths[i], splits);
34540e8eefbedcafc51948945647d746daaee092f16Adam Lesinski        const size_t apkSplitDescriptionCount = splits.size();
34640e8eefbedcafc51948945647d746daaee092f16Adam Lesinski        for (size_t j = 0; j < apkSplitDescriptionCount; j++) {
34740e8eefbedcafc51948945647d746daaee092f16Adam Lesinski            splitApkPathMap.replaceValueFor(splits[j], splitApkPaths[i]);
34840e8eefbedcafc51948945647d746daaee092f16Adam Lesinski        }
34940e8eefbedcafc51948945647d746daaee092f16Adam Lesinski        splitConfigs.appendVector(splits);
35040e8eefbedcafc51948945647d746daaee092f16Adam Lesinski    }
35140e8eefbedcafc51948945647d746daaee092f16Adam Lesinski
35240e8eefbedcafc51948945647d746daaee092f16Adam Lesinski    if (!generateFlag) {
35340e8eefbedcafc51948945647d746daaee092f16Adam Lesinski        Vector<SplitDescription> matchingConfigs = select(targetSplit, splitConfigs);
35440e8eefbedcafc51948945647d746daaee092f16Adam Lesinski        const size_t matchingConfigCount = matchingConfigs.size();
35540e8eefbedcafc51948945647d746daaee092f16Adam Lesinski        SortedVector<String8> matchingSplitPaths;
35640e8eefbedcafc51948945647d746daaee092f16Adam Lesinski        for (size_t i = 0; i < matchingConfigCount; i++) {
35740e8eefbedcafc51948945647d746daaee092f16Adam Lesinski            matchingSplitPaths.add(splitApkPathMap.valueFor(matchingConfigs[i]));
35840e8eefbedcafc51948945647d746daaee092f16Adam Lesinski        }
35940e8eefbedcafc51948945647d746daaee092f16Adam Lesinski
36040e8eefbedcafc51948945647d746daaee092f16Adam Lesinski        const size_t matchingSplitApkPathCount = matchingSplitPaths.size();
36140e8eefbedcafc51948945647d746daaee092f16Adam Lesinski        for (size_t i = 0; i < matchingSplitApkPathCount; i++) {
36242eea270a0a2bc54f454312817c41ac357e3a884Adam Lesinski            if (matchingSplitPaths[i] != baseApkPath) {
36342eea270a0a2bc54f454312817c41ac357e3a884Adam Lesinski                fprintf(stdout, "%s\n", matchingSplitPaths[i].string());
36442eea270a0a2bc54f454312817c41ac357e3a884Adam Lesinski            }
36540e8eefbedcafc51948945647d746daaee092f16Adam Lesinski        }
36640e8eefbedcafc51948945647d746daaee092f16Adam Lesinski    } else {
36742eea270a0a2bc54f454312817c41ac357e3a884Adam Lesinski        generate(apkPathSplitMap, baseApkPath);
36840e8eefbedcafc51948945647d746daaee092f16Adam Lesinski    }
36940e8eefbedcafc51948945647d746daaee092f16Adam Lesinski    return 0;
37040e8eefbedcafc51948945647d746daaee092f16Adam Lesinski}
37140e8eefbedcafc51948945647d746daaee092f16Adam Lesinski
37240e8eefbedcafc51948945647d746daaee092f16Adam Lesinski} // namespace split
37340e8eefbedcafc51948945647d746daaee092f16Adam Lesinski
37440e8eefbedcafc51948945647d746daaee092f16Adam Lesinskiint main(int argc, char** argv) {
37540e8eefbedcafc51948945647d746daaee092f16Adam Lesinski    return split::main(argc, argv);
37640e8eefbedcafc51948945647d746daaee092f16Adam Lesinski}
377