11cfa54374f5ba63d69d6fcca767c4f6647cb6de2Pierre Imai/*
21cfa54374f5ba63d69d6fcca767c4f6647cb6de2Pierre Imai * Copyright (C) 2016 The Android Open Source Project
31cfa54374f5ba63d69d6fcca767c4f6647cb6de2Pierre Imai *
41cfa54374f5ba63d69d6fcca767c4f6647cb6de2Pierre Imai * Licensed under the Apache License, Version 2.0 (the "License");
51cfa54374f5ba63d69d6fcca767c4f6647cb6de2Pierre Imai * you may not use this file except in compliance with the License.
61cfa54374f5ba63d69d6fcca767c4f6647cb6de2Pierre Imai * You may obtain a copy of the License at
71cfa54374f5ba63d69d6fcca767c4f6647cb6de2Pierre Imai *
81cfa54374f5ba63d69d6fcca767c4f6647cb6de2Pierre Imai *      http://www.apache.org/licenses/LICENSE-2.0
91cfa54374f5ba63d69d6fcca767c4f6647cb6de2Pierre Imai *
101cfa54374f5ba63d69d6fcca767c4f6647cb6de2Pierre Imai * Unless required by applicable law or agreed to in writing, software
111cfa54374f5ba63d69d6fcca767c4f6647cb6de2Pierre Imai * distributed under the License is distributed on an "AS IS" BASIS,
121cfa54374f5ba63d69d6fcca767c4f6647cb6de2Pierre Imai * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
131cfa54374f5ba63d69d6fcca767c4f6647cb6de2Pierre Imai * See the License for the specific language governing permissions and
141cfa54374f5ba63d69d6fcca767c4f6647cb6de2Pierre Imai * limitations under the License.
151cfa54374f5ba63d69d6fcca767c4f6647cb6de2Pierre Imai */
161cfa54374f5ba63d69d6fcca767c4f6647cb6de2Pierre Imai
1705306fb80d3b08775a6899384700ecaa8d644b62Lorenzo Colitti#include <android-base/stringprintf.h>
1805306fb80d3b08775a6899384700ecaa8d644b62Lorenzo Colitti
1919ee8a8dd675ecf788a5a527b843b59fbbe3255fLorenzo Colitti#define LOG_TAG "Netd"
2019ee8a8dd675ecf788a5a527b843b59fbbe3255fLorenzo Colitti#include <cutils/log.h>
2119ee8a8dd675ecf788a5a527b843b59fbbe3255fLorenzo Colitti
221cfa54374f5ba63d69d6fcca767c4f6647cb6de2Pierre Imai#include "Controllers.h"
231ed96e2d3fa89ca4848750a6b3bbbcf677946d27Lorenzo Colitti#include "IdletimerController.h"
241ed96e2d3fa89ca4848750a6b3bbbcf677946d27Lorenzo Colitti#include "NetworkController.h"
251ed96e2d3fa89ca4848750a6b3bbbcf677946d27Lorenzo Colitti#include "RouteController.h"
2619ee8a8dd675ecf788a5a527b843b59fbbe3255fLorenzo Colitti#include "Stopwatch.h"
271ed96e2d3fa89ca4848750a6b3bbbcf677946d27Lorenzo Colitti#include "oem_iptables_hook.h"
281cfa54374f5ba63d69d6fcca767c4f6647cb6de2Pierre Imai
291cfa54374f5ba63d69d6fcca767c4f6647cb6de2Pierre Imainamespace android {
301cfa54374f5ba63d69d6fcca767c4f6647cb6de2Pierre Imainamespace net {
311cfa54374f5ba63d69d6fcca767c4f6647cb6de2Pierre Imai
321ed96e2d3fa89ca4848750a6b3bbbcf677946d27Lorenzo Colittinamespace {
331ed96e2d3fa89ca4848750a6b3bbbcf677946d27Lorenzo Colitti/**
341ed96e2d3fa89ca4848750a6b3bbbcf677946d27Lorenzo Colitti * List of module chains to be created, along with explicit ordering. ORDERING
351ed96e2d3fa89ca4848750a6b3bbbcf677946d27Lorenzo Colitti * IS CRITICAL, AND SHOULD BE TRIPLE-CHECKED WITH EACH CHANGE.
361ed96e2d3fa89ca4848750a6b3bbbcf677946d27Lorenzo Colitti */
371ed96e2d3fa89ca4848750a6b3bbbcf677946d27Lorenzo Colittistatic const char* FILTER_INPUT[] = {
381ed96e2d3fa89ca4848750a6b3bbbcf677946d27Lorenzo Colitti        // Bandwidth should always be early in input chain, to make sure we
391ed96e2d3fa89ca4848750a6b3bbbcf677946d27Lorenzo Colitti        // correctly count incoming traffic against data plan.
401ed96e2d3fa89ca4848750a6b3bbbcf677946d27Lorenzo Colitti        BandwidthController::LOCAL_INPUT,
411ed96e2d3fa89ca4848750a6b3bbbcf677946d27Lorenzo Colitti        FirewallController::LOCAL_INPUT,
421ed96e2d3fa89ca4848750a6b3bbbcf677946d27Lorenzo Colitti        NULL,
431ed96e2d3fa89ca4848750a6b3bbbcf677946d27Lorenzo Colitti};
441ed96e2d3fa89ca4848750a6b3bbbcf677946d27Lorenzo Colitti
451ed96e2d3fa89ca4848750a6b3bbbcf677946d27Lorenzo Colittistatic const char* FILTER_FORWARD[] = {
461ed96e2d3fa89ca4848750a6b3bbbcf677946d27Lorenzo Colitti        OEM_IPTABLES_FILTER_FORWARD,
471ed96e2d3fa89ca4848750a6b3bbbcf677946d27Lorenzo Colitti        FirewallController::LOCAL_FORWARD,
481ed96e2d3fa89ca4848750a6b3bbbcf677946d27Lorenzo Colitti        BandwidthController::LOCAL_FORWARD,
491ed96e2d3fa89ca4848750a6b3bbbcf677946d27Lorenzo Colitti        NatController::LOCAL_FORWARD,
501ed96e2d3fa89ca4848750a6b3bbbcf677946d27Lorenzo Colitti        NULL,
511ed96e2d3fa89ca4848750a6b3bbbcf677946d27Lorenzo Colitti};
521ed96e2d3fa89ca4848750a6b3bbbcf677946d27Lorenzo Colitti
531ed96e2d3fa89ca4848750a6b3bbbcf677946d27Lorenzo Colittistatic const char* FILTER_OUTPUT[] = {
541ed96e2d3fa89ca4848750a6b3bbbcf677946d27Lorenzo Colitti        OEM_IPTABLES_FILTER_OUTPUT,
551ed96e2d3fa89ca4848750a6b3bbbcf677946d27Lorenzo Colitti        FirewallController::LOCAL_OUTPUT,
561ed96e2d3fa89ca4848750a6b3bbbcf677946d27Lorenzo Colitti        StrictController::LOCAL_OUTPUT,
571ed96e2d3fa89ca4848750a6b3bbbcf677946d27Lorenzo Colitti        BandwidthController::LOCAL_OUTPUT,
581ed96e2d3fa89ca4848750a6b3bbbcf677946d27Lorenzo Colitti        NULL,
591ed96e2d3fa89ca4848750a6b3bbbcf677946d27Lorenzo Colitti};
601ed96e2d3fa89ca4848750a6b3bbbcf677946d27Lorenzo Colitti
611ed96e2d3fa89ca4848750a6b3bbbcf677946d27Lorenzo Colittistatic const char* RAW_PREROUTING[] = {
621ed96e2d3fa89ca4848750a6b3bbbcf677946d27Lorenzo Colitti        BandwidthController::LOCAL_RAW_PREROUTING,
631ed96e2d3fa89ca4848750a6b3bbbcf677946d27Lorenzo Colitti        IdletimerController::LOCAL_RAW_PREROUTING,
641ed96e2d3fa89ca4848750a6b3bbbcf677946d27Lorenzo Colitti        NatController::LOCAL_RAW_PREROUTING,
651ed96e2d3fa89ca4848750a6b3bbbcf677946d27Lorenzo Colitti        NULL,
661ed96e2d3fa89ca4848750a6b3bbbcf677946d27Lorenzo Colitti};
671ed96e2d3fa89ca4848750a6b3bbbcf677946d27Lorenzo Colitti
681ed96e2d3fa89ca4848750a6b3bbbcf677946d27Lorenzo Colittistatic const char* MANGLE_POSTROUTING[] = {
6905306fb80d3b08775a6899384700ecaa8d644b62Lorenzo Colitti        OEM_IPTABLES_MANGLE_POSTROUTING,
701ed96e2d3fa89ca4848750a6b3bbbcf677946d27Lorenzo Colitti        BandwidthController::LOCAL_MANGLE_POSTROUTING,
711ed96e2d3fa89ca4848750a6b3bbbcf677946d27Lorenzo Colitti        IdletimerController::LOCAL_MANGLE_POSTROUTING,
721ed96e2d3fa89ca4848750a6b3bbbcf677946d27Lorenzo Colitti        NULL,
731ed96e2d3fa89ca4848750a6b3bbbcf677946d27Lorenzo Colitti};
741ed96e2d3fa89ca4848750a6b3bbbcf677946d27Lorenzo Colitti
751ed96e2d3fa89ca4848750a6b3bbbcf677946d27Lorenzo Colittistatic const char* MANGLE_FORWARD[] = {
761ed96e2d3fa89ca4848750a6b3bbbcf677946d27Lorenzo Colitti        NatController::LOCAL_MANGLE_FORWARD,
771ed96e2d3fa89ca4848750a6b3bbbcf677946d27Lorenzo Colitti        NULL,
781ed96e2d3fa89ca4848750a6b3bbbcf677946d27Lorenzo Colitti};
791ed96e2d3fa89ca4848750a6b3bbbcf677946d27Lorenzo Colitti
801ed96e2d3fa89ca4848750a6b3bbbcf677946d27Lorenzo Colittistatic const char* NAT_PREROUTING[] = {
811ed96e2d3fa89ca4848750a6b3bbbcf677946d27Lorenzo Colitti        OEM_IPTABLES_NAT_PREROUTING,
821ed96e2d3fa89ca4848750a6b3bbbcf677946d27Lorenzo Colitti        NULL,
831ed96e2d3fa89ca4848750a6b3bbbcf677946d27Lorenzo Colitti};
841ed96e2d3fa89ca4848750a6b3bbbcf677946d27Lorenzo Colitti
851ed96e2d3fa89ca4848750a6b3bbbcf677946d27Lorenzo Colittistatic const char* NAT_POSTROUTING[] = {
861ed96e2d3fa89ca4848750a6b3bbbcf677946d27Lorenzo Colitti        NatController::LOCAL_NAT_POSTROUTING,
871ed96e2d3fa89ca4848750a6b3bbbcf677946d27Lorenzo Colitti        NULL,
881ed96e2d3fa89ca4848750a6b3bbbcf677946d27Lorenzo Colitti};
891ed96e2d3fa89ca4848750a6b3bbbcf677946d27Lorenzo Colitti
901ed96e2d3fa89ca4848750a6b3bbbcf677946d27Lorenzo Colittistatic void createChildChains(IptablesTarget target, const char* table, const char* parentChain,
91cda022e61c37cc6e6bd863c5a6525108e3789ca8Lorenzo Colitti        const char** childChains, bool exclusive) {
92cda022e61c37cc6e6bd863c5a6525108e3789ca8Lorenzo Colitti    std::string command = android::base::StringPrintf("*%s\n", table);
93cda022e61c37cc6e6bd863c5a6525108e3789ca8Lorenzo Colitti
94cda022e61c37cc6e6bd863c5a6525108e3789ca8Lorenzo Colitti    // If we're the exclusive owner of this chain, clear it entirely. This saves us from having to
95cda022e61c37cc6e6bd863c5a6525108e3789ca8Lorenzo Colitti    // run one execIptablesSilently command to delete each child chain. We can't use -D in
96cda022e61c37cc6e6bd863c5a6525108e3789ca8Lorenzo Colitti    // iptables-restore because it's a fatal error if the rule doesn't exist.
97cda022e61c37cc6e6bd863c5a6525108e3789ca8Lorenzo Colitti    // TODO: Make all chains exclusive once vendor code uses the oem_* rules.
98cda022e61c37cc6e6bd863c5a6525108e3789ca8Lorenzo Colitti    if (exclusive) {
99cda022e61c37cc6e6bd863c5a6525108e3789ca8Lorenzo Colitti        // Just running ":chain -" flushes user-defined chains, but not built-in chains like INPUT.
100cda022e61c37cc6e6bd863c5a6525108e3789ca8Lorenzo Colitti        // Since at this point we don't know if parentChain is a built-in chain, do both.
101cda022e61c37cc6e6bd863c5a6525108e3789ca8Lorenzo Colitti        command += android::base::StringPrintf(":%s -\n", parentChain);
102cda022e61c37cc6e6bd863c5a6525108e3789ca8Lorenzo Colitti        command += android::base::StringPrintf("-F %s\n", parentChain);
103cda022e61c37cc6e6bd863c5a6525108e3789ca8Lorenzo Colitti    }
1041ed96e2d3fa89ca4848750a6b3bbbcf677946d27Lorenzo Colitti
10505306fb80d3b08775a6899384700ecaa8d644b62Lorenzo Colitti    const char** childChain = childChains;
10605306fb80d3b08775a6899384700ecaa8d644b62Lorenzo Colitti    do {
107cda022e61c37cc6e6bd863c5a6525108e3789ca8Lorenzo Colitti        if (!exclusive) {
108cda022e61c37cc6e6bd863c5a6525108e3789ca8Lorenzo Colitti            execIptablesSilently(target, "-t", table, "-D", parentChain, "-j", *childChain, NULL);
109cda022e61c37cc6e6bd863c5a6525108e3789ca8Lorenzo Colitti        }
11005306fb80d3b08775a6899384700ecaa8d644b62Lorenzo Colitti        command += android::base::StringPrintf(":%s -\n", *childChain);
11105306fb80d3b08775a6899384700ecaa8d644b62Lorenzo Colitti        command += android::base::StringPrintf("-A %s -j %s\n", parentChain, *childChain);
11205306fb80d3b08775a6899384700ecaa8d644b62Lorenzo Colitti    } while (*(++childChain) != NULL);
11305306fb80d3b08775a6899384700ecaa8d644b62Lorenzo Colitti    command += "COMMIT\n\n";
11405306fb80d3b08775a6899384700ecaa8d644b62Lorenzo Colitti    execIptablesRestore(target, command);
11505306fb80d3b08775a6899384700ecaa8d644b62Lorenzo Colitti}
11605306fb80d3b08775a6899384700ecaa8d644b62Lorenzo Colitti
1171ed96e2d3fa89ca4848750a6b3bbbcf677946d27Lorenzo Colitti}  // namespace
1181ed96e2d3fa89ca4848750a6b3bbbcf677946d27Lorenzo Colitti
1192c5aaa1876db659556c2e9605beccc670e6b7c0dErik KlineControllers::Controllers() : clatdCtrl(&netCtrl) {
1202c5aaa1876db659556c2e9605beccc670e6b7c0dErik Kline    InterfaceController::initializeAll();
1212c5aaa1876db659556c2e9605beccc670e6b7c0dErik Kline}
1221cfa54374f5ba63d69d6fcca767c4f6647cb6de2Pierre Imai
1231ed96e2d3fa89ca4848750a6b3bbbcf677946d27Lorenzo Colittivoid Controllers::initIptablesRules() {
1241ed96e2d3fa89ca4848750a6b3bbbcf677946d27Lorenzo Colitti    /*
1251ed96e2d3fa89ca4848750a6b3bbbcf677946d27Lorenzo Colitti     * This is the only time we touch top-level chains in iptables; controllers
1261ed96e2d3fa89ca4848750a6b3bbbcf677946d27Lorenzo Colitti     * should only mutate rules inside of their children chains, as created by
1271ed96e2d3fa89ca4848750a6b3bbbcf677946d27Lorenzo Colitti     * the constants above.
1281ed96e2d3fa89ca4848750a6b3bbbcf677946d27Lorenzo Colitti     *
1291ed96e2d3fa89ca4848750a6b3bbbcf677946d27Lorenzo Colitti     * Modules should never ACCEPT packets (except in well-justified cases);
1301ed96e2d3fa89ca4848750a6b3bbbcf677946d27Lorenzo Colitti     * they should instead defer to any remaining modules using RETURN, or
1311ed96e2d3fa89ca4848750a6b3bbbcf677946d27Lorenzo Colitti     * otherwise DROP/REJECT.
1321ed96e2d3fa89ca4848750a6b3bbbcf677946d27Lorenzo Colitti     */
1331ed96e2d3fa89ca4848750a6b3bbbcf677946d27Lorenzo Colitti
13405306fb80d3b08775a6899384700ecaa8d644b62Lorenzo Colitti    // Create chains for child modules.
13505306fb80d3b08775a6899384700ecaa8d644b62Lorenzo Colitti    // We cannot use createChildChainsFast for all chains because vendor code modifies filter OUTPUT
13605306fb80d3b08775a6899384700ecaa8d644b62Lorenzo Colitti    // and mangle POSTROUTING directly.
13719ee8a8dd675ecf788a5a527b843b59fbbe3255fLorenzo Colitti    Stopwatch s;
138cda022e61c37cc6e6bd863c5a6525108e3789ca8Lorenzo Colitti    createChildChains(V4V6, "filter", "INPUT", FILTER_INPUT, true);
139cda022e61c37cc6e6bd863c5a6525108e3789ca8Lorenzo Colitti    createChildChains(V4V6, "filter", "FORWARD", FILTER_FORWARD, true);
140cda022e61c37cc6e6bd863c5a6525108e3789ca8Lorenzo Colitti    createChildChains(V4V6, "filter", "OUTPUT", FILTER_OUTPUT, false);
141cda022e61c37cc6e6bd863c5a6525108e3789ca8Lorenzo Colitti    createChildChains(V4V6, "raw", "PREROUTING", RAW_PREROUTING, true);
142cda022e61c37cc6e6bd863c5a6525108e3789ca8Lorenzo Colitti    createChildChains(V4V6, "mangle", "POSTROUTING", MANGLE_POSTROUTING, false);
143cda022e61c37cc6e6bd863c5a6525108e3789ca8Lorenzo Colitti    createChildChains(V4V6, "mangle", "FORWARD", MANGLE_FORWARD, true);
144cda022e61c37cc6e6bd863c5a6525108e3789ca8Lorenzo Colitti    createChildChains(V4, "nat", "PREROUTING", NAT_PREROUTING, true);
145cda022e61c37cc6e6bd863c5a6525108e3789ca8Lorenzo Colitti    createChildChains(V4, "nat", "POSTROUTING", NAT_POSTROUTING, true);
14619ee8a8dd675ecf788a5a527b843b59fbbe3255fLorenzo Colitti    ALOGI("Creating child chains: %.1fms", s.getTimeAndReset());
1471ed96e2d3fa89ca4848750a6b3bbbcf677946d27Lorenzo Colitti
1481ed96e2d3fa89ca4848750a6b3bbbcf677946d27Lorenzo Colitti    // Let each module setup their child chains
1491ed96e2d3fa89ca4848750a6b3bbbcf677946d27Lorenzo Colitti    setupOemIptablesHook();
15019ee8a8dd675ecf788a5a527b843b59fbbe3255fLorenzo Colitti    ALOGI("Setting up OEM hooks: %.1fms", s.getTimeAndReset());
1511ed96e2d3fa89ca4848750a6b3bbbcf677946d27Lorenzo Colitti
1521ed96e2d3fa89ca4848750a6b3bbbcf677946d27Lorenzo Colitti    /* When enabled, DROPs all packets except those matching rules. */
1531ed96e2d3fa89ca4848750a6b3bbbcf677946d27Lorenzo Colitti    firewallCtrl.setupIptablesHooks();
15419ee8a8dd675ecf788a5a527b843b59fbbe3255fLorenzo Colitti    ALOGI("Setting up FirewallController hooks: %.1fms", s.getTimeAndReset());
1551ed96e2d3fa89ca4848750a6b3bbbcf677946d27Lorenzo Colitti
1561ed96e2d3fa89ca4848750a6b3bbbcf677946d27Lorenzo Colitti    /* Does DROPs in FORWARD by default */
1571ed96e2d3fa89ca4848750a6b3bbbcf677946d27Lorenzo Colitti    natCtrl.setupIptablesHooks();
15819ee8a8dd675ecf788a5a527b843b59fbbe3255fLorenzo Colitti    ALOGI("Setting up NatController hooks: %.1fms", s.getTimeAndReset());
15919ee8a8dd675ecf788a5a527b843b59fbbe3255fLorenzo Colitti
1601ed96e2d3fa89ca4848750a6b3bbbcf677946d27Lorenzo Colitti    /*
1611ed96e2d3fa89ca4848750a6b3bbbcf677946d27Lorenzo Colitti     * Does REJECT in INPUT, OUTPUT. Does counting also.
1621ed96e2d3fa89ca4848750a6b3bbbcf677946d27Lorenzo Colitti     * No DROP/REJECT allowed later in netfilter-flow hook order.
1631ed96e2d3fa89ca4848750a6b3bbbcf677946d27Lorenzo Colitti     */
1641ed96e2d3fa89ca4848750a6b3bbbcf677946d27Lorenzo Colitti    bandwidthCtrl.setupIptablesHooks();
16519ee8a8dd675ecf788a5a527b843b59fbbe3255fLorenzo Colitti    ALOGI("Setting up BandwidthController hooks: %.1fms", s.getTimeAndReset());
16619ee8a8dd675ecf788a5a527b843b59fbbe3255fLorenzo Colitti
1671ed96e2d3fa89ca4848750a6b3bbbcf677946d27Lorenzo Colitti    /*
1681ed96e2d3fa89ca4848750a6b3bbbcf677946d27Lorenzo Colitti     * Counts in nat: PREROUTING, POSTROUTING.
1691ed96e2d3fa89ca4848750a6b3bbbcf677946d27Lorenzo Colitti     * No DROP/REJECT allowed later in netfilter-flow hook order.
1701ed96e2d3fa89ca4848750a6b3bbbcf677946d27Lorenzo Colitti     */
1711ed96e2d3fa89ca4848750a6b3bbbcf677946d27Lorenzo Colitti    idletimerCtrl.setupIptablesHooks();
17219ee8a8dd675ecf788a5a527b843b59fbbe3255fLorenzo Colitti    ALOGI("Setting up IdletimerController hooks: %.1fms", s.getTimeAndReset());
1731ed96e2d3fa89ca4848750a6b3bbbcf677946d27Lorenzo Colitti}
1741ed96e2d3fa89ca4848750a6b3bbbcf677946d27Lorenzo Colitti
1751ed96e2d3fa89ca4848750a6b3bbbcf677946d27Lorenzo Colittivoid Controllers::init() {
1761ed96e2d3fa89ca4848750a6b3bbbcf677946d27Lorenzo Colitti    initIptablesRules();
17719ee8a8dd675ecf788a5a527b843b59fbbe3255fLorenzo Colitti
17819ee8a8dd675ecf788a5a527b843b59fbbe3255fLorenzo Colitti    Stopwatch s;
1791ed96e2d3fa89ca4848750a6b3bbbcf677946d27Lorenzo Colitti    bandwidthCtrl.enableBandwidthControl(false);
18019ee8a8dd675ecf788a5a527b843b59fbbe3255fLorenzo Colitti    ALOGI("Disabling bandwidth control: %.1fms", s.getTimeAndReset());
1811ed96e2d3fa89ca4848750a6b3bbbcf677946d27Lorenzo Colitti
1821ed96e2d3fa89ca4848750a6b3bbbcf677946d27Lorenzo Colitti    if (int ret = RouteController::Init(NetworkController::LOCAL_NET_ID)) {
1831ed96e2d3fa89ca4848750a6b3bbbcf677946d27Lorenzo Colitti        ALOGE("failed to initialize RouteController (%s)", strerror(-ret));
1841ed96e2d3fa89ca4848750a6b3bbbcf677946d27Lorenzo Colitti    }
18519ee8a8dd675ecf788a5a527b843b59fbbe3255fLorenzo Colitti    ALOGI("Initializing RouteController: %.1fms", s.getTimeAndReset());
1861ed96e2d3fa89ca4848750a6b3bbbcf677946d27Lorenzo Colitti}
1871ed96e2d3fa89ca4848750a6b3bbbcf677946d27Lorenzo Colitti
1881cfa54374f5ba63d69d6fcca767c4f6647cb6de2Pierre ImaiControllers* gCtls = nullptr;
1891cfa54374f5ba63d69d6fcca767c4f6647cb6de2Pierre Imai
1901cfa54374f5ba63d69d6fcca767c4f6647cb6de2Pierre Imai}  // namespace net
1911cfa54374f5ba63d69d6fcca767c4f6647cb6de2Pierre Imai}  // namespace android
192