Snap for 8683397 from 5caee32dfdd6c91490f58f7b1f3b6e759b006880 to mainline-media-release

Change-Id: I8ca683b3de08d607d78429a9e3016dfadd04edb9
diff --git a/server/MDnsEventReporter.cpp b/server/MDnsEventReporter.cpp
index db9021a..e94de36 100644
--- a/server/MDnsEventReporter.cpp
+++ b/server/MDnsEventReporter.cpp
@@ -18,6 +18,8 @@
 
 #include "MDnsEventReporter.h"
 
+using android::IInterface;
+using android::sp;
 using android::net::mdns::aidl::IMDnsEventListener;
 
 MDnsEventReporter& MDnsEventReporter::getInstance() {
@@ -30,11 +32,11 @@
     return getEventListenersImpl();
 }
 
-int MDnsEventReporter::addEventListener(const android::sp<IMDnsEventListener>& listener) {
+int MDnsEventReporter::addEventListener(const sp<IMDnsEventListener>& listener) {
     return addEventListenerImpl(listener);
 }
 
-int MDnsEventReporter::removeEventListener(const android::sp<IMDnsEventListener>& listener) {
+int MDnsEventReporter::removeEventListener(const sp<IMDnsEventListener>& listener) {
     return removeEventListenerImpl(listener);
 }
 
@@ -43,7 +45,7 @@
     return mEventListeners;
 }
 
-int MDnsEventReporter::addEventListenerImpl(const android::sp<IMDnsEventListener>& listener) {
+int MDnsEventReporter::addEventListenerImpl(const sp<IMDnsEventListener>& listener) {
     if (listener == nullptr) {
         ALOGE("The event listener should not be null");
         return -EINVAL;
@@ -52,39 +54,19 @@
     std::lock_guard lock(mMutex);
 
     for (const auto& it : mEventListeners) {
-        if (android::IInterface::asBinder(it).get() ==
-            android::IInterface::asBinder(listener).get()) {
+        if (IInterface::asBinder(it->getListener()).get() == IInterface::asBinder(listener).get()) {
             ALOGW("The event listener was already subscribed");
             return -EEXIST;
         }
     }
 
-    // Create the death listener.
-    class DeathRecipient : public android::IBinder::DeathRecipient {
-      public:
-        DeathRecipient(MDnsEventReporter* eventReporter,
-                       const android::sp<IMDnsEventListener>& listener)
-            : mEventReporter(eventReporter), mListener(listener) {}
-        ~DeathRecipient() override = default;
-        void binderDied(const android::wp<android::IBinder>& /* who */) override {
-            mEventReporter->removeEventListenerImpl(mListener);
-        }
-
-      private:
-        MDnsEventReporter* mEventReporter;
-        android::sp<IMDnsEventListener> mListener;
-    };
-
-    android::sp<android::IBinder::DeathRecipient> deathRecipient =
-            new DeathRecipient(this, listener);
-
-    android::IInterface::asBinder(listener)->linkToDeath(deathRecipient);
-
-    mEventListeners.insert(listener);
+    auto eventListener = sp<EventListener>::make(this, listener);
+    IInterface::asBinder(listener)->linkToDeath(eventListener);
+    mEventListeners.insert(eventListener);
     return 0;
 }
 
-int MDnsEventReporter::removeEventListenerImpl(const android::sp<IMDnsEventListener>& listener) {
+int MDnsEventReporter::removeEventListenerImpl(const sp<IMDnsEventListener>& listener) {
     if (listener == nullptr) {
         ALOGE("The event listener should not be null");
         return -EINVAL;
@@ -92,6 +74,14 @@
 
     std::lock_guard lock(mMutex);
 
-    mEventListeners.erase(listener);
-    return 0;
-}
+    for (const auto& it : mEventListeners) {
+        const auto& binder = IInterface::asBinder(it->getListener());
+        if (binder == IInterface::asBinder(listener)) {
+            binder->unlinkToDeath(it);
+            mEventListeners.erase(it);
+            return 0;
+        }
+    }
+    ALOGE("The event listener does not exist");
+    return -ENOENT;
+}
\ No newline at end of file
diff --git a/server/MDnsEventReporter.h b/server/MDnsEventReporter.h
index e49c3e3..cbc43ec 100644
--- a/server/MDnsEventReporter.h
+++ b/server/MDnsEventReporter.h
@@ -23,10 +23,28 @@
 
 class MDnsEventReporter final {
   public:
+    class EventListener : public android::IBinder::DeathRecipient {
+      public:
+        EventListener(MDnsEventReporter* eventReporter,
+                      const android::sp<android::net::mdns::aidl::IMDnsEventListener>& listener)
+            : mEventReporter(eventReporter), mListener(listener) {}
+        ~EventListener() override = default;
+        void binderDied(const android::wp<android::IBinder>& /* who */) override {
+            mEventReporter->removeEventListenerImpl(mListener);
+        }
+        android::sp<android::net::mdns::aidl::IMDnsEventListener> getListener() {
+            return mListener;
+        }
+
+      private:
+        MDnsEventReporter* mEventReporter;
+        android::sp<android::net::mdns::aidl::IMDnsEventListener> mListener;
+    };
+
     MDnsEventReporter(const MDnsEventReporter&) = delete;
     MDnsEventReporter& operator=(const MDnsEventReporter&) = delete;
 
-    using EventListenerSet = std::set<android::sp<android::net::mdns::aidl::IMDnsEventListener>>;
+    using EventListenerSet = std::set<android::sp<EventListener>>;
 
     // Get the instance of the singleton MDnsEventReporter.
     static MDnsEventReporter& getInstance();
@@ -56,5 +74,4 @@
             const android::sp<android::net::mdns::aidl::IMDnsEventListener>& listener)
             EXCLUDES(mMutex);
     const EventListenerSet& getEventListenersImpl() const EXCLUDES(mMutex);
-    void handleEventBinderDied(const void* who) EXCLUDES(mMutex);
 };
diff --git a/server/MDnsSdListener.cpp b/server/MDnsSdListener.cpp
index 1636400..1d1ea40 100644
--- a/server/MDnsSdListener.cpp
+++ b/server/MDnsSdListener.cpp
@@ -140,7 +140,7 @@
     }
 
     for (const auto& it : listeners) {
-        it->onServiceDiscoveryStatus(info);
+        it->getListener()->onServiceDiscoveryStatus(info);
     }
 }
 
@@ -212,7 +212,7 @@
     }
 
     for (const auto& it : listeners) {
-        it->onServiceRegistrationStatus(info);
+        it->getListener()->onServiceRegistrationStatus(info);
     }
 }
 
@@ -277,7 +277,7 @@
     }
 
     for (const auto& it : listeners) {
-        it->onServiceResolutionStatus(info);
+        it->getListener()->onServiceResolutionStatus(info);
     }
 }
 
@@ -343,7 +343,7 @@
         info.result = IMDnsEventListener::SERVICE_GET_ADDR_FAILED;
     }
     for (const auto& it : listeners) {
-        it->onGettingServiceAddressStatus(info);
+        it->getListener()->onGettingServiceAddressStatus(info);
     }
 }
 
diff --git a/server/RouteController.cpp b/server/RouteController.cpp
index 5ed33cd..d2af9a3 100644
--- a/server/RouteController.cpp
+++ b/server/RouteController.cpp
@@ -21,9 +21,9 @@
 #include <fcntl.h>
 #include <linux/fib_rules.h>
 #include <net/if.h>
-#include <sys/stat.h>
-
+#include <netdutils/InternetAddresses.h>
 #include <private/android_filesystem_config.h>
+#include <sys/stat.h>
 
 #include <map>
 
@@ -43,6 +43,7 @@
 using android::base::StartsWith;
 using android::base::StringPrintf;
 using android::base::WriteStringToFile;
+using android::netdutils::IPPrefix;
 
 namespace android::net {
 
@@ -63,6 +64,14 @@
 
 const char* const RouteController::LOCAL_MANGLE_INPUT = "routectrl_mangle_INPUT";
 
+const IPPrefix V4_LOCAL_ADDR[] = {
+        IPPrefix::forString("169.254.0.0/16"),  // Link Local
+        IPPrefix::forString("100.64.0.0/10"),   // CGNAT
+        IPPrefix::forString("10.0.0.0/8"),      // RFC1918
+        IPPrefix::forString("172.16.0.0/12"),   // RFC1918
+        IPPrefix::forString("192.168.0.0/16")   // RFC1918
+};
+
 const uint8_t AF_FAMILIES[] = {AF_INET, AF_INET6};
 
 const uid_t UID_ROOT = 0;
@@ -859,6 +868,13 @@
                                                           subPriority, add)) {
                     return ret;
                 }
+
+                // Per-UID local network rules must always match per-app default network rules,
+                // because their purpose is to allow the UIDs to use the default network for
+                // local destinations within it.
+                if (int ret = modifyUidLocalNetworkRule(interface, range.start, range.stop, add)) {
+                    return ret;
+                }
             }
         }
     }
@@ -906,6 +922,32 @@
     return 0;
 }
 
+int RouteController::modifyUidLocalNetworkRule(const char* interface, uid_t uidStart, uid_t uidEnd,
+                                               bool add) {
+    uint32_t table = getRouteTableForInterface(interface, true /* local */);
+    if (table == RT_TABLE_UNSPEC) {
+        return -ESRCH;
+    }
+
+    if ((uidStart == INVALID_UID) || (uidEnd == INVALID_UID)) {
+        ALOGE("modifyUidLocalNetworkRule, invalid UIDs (%u, %u)", uidStart, uidEnd);
+        return -EUSERS;
+    }
+
+    Fwmark fwmark;
+    Fwmark mask;
+
+    fwmark.explicitlySelected = false;
+    mask.explicitlySelected = true;
+
+    // Access to this network is controlled by UID rules, not permission bits.
+    fwmark.permission = PERMISSION_NONE;
+    mask.permission = PERMISSION_NONE;
+
+    return modifyIpRule(add ? RTM_NEWRULE : RTM_DELRULE, RULE_PRIORITY_UID_LOCAL_ROUTES, table,
+                        fwmark.intValue, mask.intValue, IIF_LOOPBACK, OIF_NONE, uidStart, uidEnd);
+}
+
 [[nodiscard]] static int modifyUidUnreachableRule(unsigned netId, uid_t uidStart, uid_t uidEnd,
                                                   int32_t subPriority, bool add,
                                                   bool explicitSelect) {
@@ -1074,11 +1116,11 @@
 // Returns 0 on success or negative errno on failure.
 int RouteController::modifyRoute(uint16_t action, uint16_t flags, const char* interface,
                                  const char* destination, const char* nexthop, TableType tableType,
-                                 int mtu, int priority) {
+                                 int mtu, int priority, bool isLocal) {
     uint32_t table;
     switch (tableType) {
         case RouteController::INTERFACE: {
-            table = getRouteTableForInterface(interface, false /* local */);
+            table = getRouteTableForInterface(interface, isLocal);
             if (table == RT_TABLE_UNSPEC) {
                 return -ESRCH;
             }
@@ -1252,10 +1294,6 @@
                                         MODIFY_NON_UID_BASED_RULES)) {
         return ret;
     }
-    // TODO: Consider to remove regular table if adding local table failed.
-    if (int ret = modifyVpnLocalExclusionRule(true, interface)) {
-        return ret;
-    }
 
     maybeModifyQdiscClsact(interface, ACTION_ADD);
     updateTableNamesFile();
@@ -1270,10 +1308,7 @@
         return ret;
     }
 
-    int ret = modifyVpnLocalExclusionRule(false, interface);
-    // Always perform flushRoute even if removing local exclusion rules failed.
-    ret |= flushRoutes(interface);
-    if (ret) {
+    if (int ret = flushRoutes(interface)) {
         return ret;
     }
 
@@ -1357,22 +1392,66 @@
     return modifyDefaultNetwork(RTM_DELRULE, interface, permission);
 }
 
+bool RouteController::isTargetV4LocalRange(const char* dst) {
+    for (IPPrefix addr : V4_LOCAL_ADDR) {
+        if (addr.contains(IPPrefix::forString(dst))) {
+            return true;
+        }
+    }
+    return false;
+}
+
+bool RouteController::isLocalAddress(TableType tableType, const char* destination,
+                                     const char* nexthop) {
+    IPPrefix prefix = IPPrefix::forString(destination);
+    return nexthop == nullptr && tableType == RouteController::INTERFACE &&
+           // Skip default route to prevent network being modeled as point-to-point interfaces.
+           ((prefix.family() == AF_INET6 && prefix != IPPrefix::forString("::/0")) ||
+            // Skip adding non-target local network range.
+            (prefix.family() == AF_INET && isTargetV4LocalRange(destination)));
+}
+
 int RouteController::addRoute(const char* interface, const char* destination, const char* nexthop,
                               TableType tableType, int mtu, int priority) {
-    return modifyRoute(RTM_NEWROUTE, NETLINK_ROUTE_CREATE_FLAGS, interface, destination, nexthop,
-                       tableType, mtu, priority);
+    if (int ret = modifyRoute(RTM_NEWROUTE, NETLINK_ROUTE_CREATE_FLAGS, interface, destination,
+                              nexthop, tableType, mtu, priority, false /* isLocal */)) {
+        return ret;
+    }
+
+    if (isLocalAddress(tableType, destination, nexthop)) {
+        return modifyRoute(RTM_NEWROUTE, NETLINK_ROUTE_CREATE_FLAGS, interface, destination,
+                           nexthop, tableType, mtu, priority, true /* isLocal */);
+    }
+
+    return 0;
 }
 
 int RouteController::removeRoute(const char* interface, const char* destination,
                                  const char* nexthop, TableType tableType, int priority) {
-    return modifyRoute(RTM_DELROUTE, NETLINK_REQUEST_FLAGS, interface, destination, nexthop,
-                       tableType, 0 /* mtu */, priority);
+    if (int ret = modifyRoute(RTM_DELROUTE, NETLINK_REQUEST_FLAGS, interface, destination, nexthop,
+                              tableType, 0 /* mtu */, priority, false /* isLocal */)) {
+        return ret;
+    }
+
+    if (isLocalAddress(tableType, destination, nexthop)) {
+        return modifyRoute(RTM_DELROUTE, NETLINK_REQUEST_FLAGS, interface, destination, nexthop,
+                           tableType, 0 /* mtu */, priority, true /* isLocal */);
+    }
+    return 0;
 }
 
 int RouteController::updateRoute(const char* interface, const char* destination,
                                  const char* nexthop, TableType tableType, int mtu) {
-    return modifyRoute(RTM_NEWROUTE, NETLINK_ROUTE_REPLACE_FLAGS, interface, destination, nexthop,
-                       tableType, mtu, 0 /* priority */);
+    if (int ret = modifyRoute(RTM_NEWROUTE, NETLINK_ROUTE_REPLACE_FLAGS, interface, destination,
+                              nexthop, tableType, mtu, 0 /* priority */, false /* isLocal */)) {
+        return ret;
+    }
+
+    if (isLocalAddress(tableType, destination, nexthop)) {
+        return modifyRoute(RTM_NEWROUTE, NETLINK_ROUTE_REPLACE_FLAGS, interface, destination,
+                           nexthop, tableType, mtu, 0 /* priority */, true /* isLocal */);
+    }
+    return 0;
 }
 
 int RouteController::enableTethering(const char* inputInterface, const char* outputInterface) {
@@ -1385,13 +1464,21 @@
 
 int RouteController::addVirtualNetworkFallthrough(unsigned vpnNetId, const char* physicalInterface,
                                                   Permission permission) {
-    return modifyVpnFallthroughRule(RTM_NEWRULE, vpnNetId, physicalInterface, permission);
+    if (int ret = modifyVpnFallthroughRule(RTM_NEWRULE, vpnNetId, physicalInterface, permission)) {
+        return ret;
+    }
+
+    return modifyVpnLocalExclusionRule(true /* add */, physicalInterface);
 }
 
 int RouteController::removeVirtualNetworkFallthrough(unsigned vpnNetId,
                                                      const char* physicalInterface,
                                                      Permission permission) {
-    return modifyVpnFallthroughRule(RTM_DELRULE, vpnNetId, physicalInterface, permission);
+    if (int ret = modifyVpnFallthroughRule(RTM_DELRULE, vpnNetId, physicalInterface, permission)) {
+        return ret;
+    }
+
+    return modifyVpnLocalExclusionRule(false /* add */, physicalInterface);
 }
 
 int RouteController::addUsersToPhysicalNetwork(unsigned netId, const char* interface,
diff --git a/server/RouteController.h b/server/RouteController.h
index 9b04cfd..ff41678 100644
--- a/server/RouteController.h
+++ b/server/RouteController.h
@@ -55,11 +55,17 @@
 constexpr int32_t RULE_PRIORITY_UID_IMPLICIT_NETWORK              = 22000;
 constexpr int32_t RULE_PRIORITY_IMPLICIT_NETWORK                  = 23000;
 constexpr int32_t RULE_PRIORITY_BYPASSABLE_VPN_NO_LOCAL_EXCLUSION = 24000;
-// Rules used for excluding local route in the VPN network.
-constexpr int32_t RULE_PRIORITY_LOCAL_ROUTES                      = 25000;
-constexpr int32_t RULE_PRIORITY_BYPASSABLE_VPN_LOCAL_EXCLUSION    = 26000;
-constexpr int32_t RULE_PRIORITY_VPN_FALLTHROUGH                   = 27000;
-constexpr int32_t RULE_PRIORITY_UID_DEFAULT_NETWORK               = 28000;
+// Sets of rules used for excluding local routes from the VPN. Look up tables
+// that contain directly-connected local routes taken from the default network.
+// The first set is used for apps that have a per-UID default network. The rule
+// UID ranges match those of the per-UID default network rule for that network.
+// The second set has no UID ranges and is used for apps whose default network
+// is the system default network network.
+constexpr int32_t RULE_PRIORITY_UID_LOCAL_ROUTES                  = 25000;
+constexpr int32_t RULE_PRIORITY_LOCAL_ROUTES                      = 26000;
+constexpr int32_t RULE_PRIORITY_BYPASSABLE_VPN_LOCAL_EXCLUSION    = 27000;
+constexpr int32_t RULE_PRIORITY_VPN_FALLTHROUGH                   = 28000;
+constexpr int32_t RULE_PRIORITY_UID_DEFAULT_NETWORK               = 29000;
 // Rule used when framework wants to disable default network from specified applications. There will
 // be a small interval the same uid range exists in both UID_DEFAULT_UNREACHABLE and
 // UID_DEFAULT_NETWORK when framework is switching user preferences.
@@ -74,8 +80,8 @@
 // The priority is lower than UID_DEFAULT_NETWORK. Otherwise, the app will be told by
 // ConnectivityService that it has a network in step 1 of the second case. But if it tries to use
 // the network, it will not work. That will potentially cause a user-visible error.
-constexpr int32_t RULE_PRIORITY_UID_DEFAULT_UNREACHABLE           = 29000;
-constexpr int32_t RULE_PRIORITY_DEFAULT_NETWORK                   = 30000;
+constexpr int32_t RULE_PRIORITY_UID_DEFAULT_UNREACHABLE           = 30000;
+constexpr int32_t RULE_PRIORITY_DEFAULT_NETWORK                   = 31000;
 constexpr int32_t RULE_PRIORITY_UNREACHABLE                       = 32000;
 // clang-format on
 
@@ -211,7 +217,7 @@
     static int modifyUnreachableNetwork(unsigned netId, const UidRangeMap& uidRangeMap, bool add);
     static int modifyRoute(uint16_t action, uint16_t flags, const char* interface,
                            const char* destination, const char* nexthop, TableType tableType,
-                           int mtu, int priority);
+                           int mtu, int priority, bool isLocal);
     static int modifyTetheredNetwork(uint16_t action, const char* inputInterface,
                                      const char* outputInterface);
     static int modifyVpnFallthroughRule(uint16_t action, unsigned vpnNetId,
@@ -221,6 +227,11 @@
                                     bool modifyNonUidBasedRules, bool excludeLocalRoutes);
     static void updateTableNamesFile() EXCLUDES(sInterfaceToTableLock);
     static int modifyVpnLocalExclusionRule(bool add, const char* physicalInterface);
+
+    static int modifyUidLocalNetworkRule(const char* interface, uid_t uidStart, uid_t uidEnd,
+                                         bool add);
+    static bool isLocalAddress(TableType tableType, const char* destination, const char* nexthop);
+    static bool isTargetV4LocalRange(const char* addrstr);
 };
 
 // Public because they are called by by RouteControllerTest.cpp.
diff --git a/tests/binder_test.cpp b/tests/binder_test.cpp
index 519effe..f423ea3 100644
--- a/tests/binder_test.cpp
+++ b/tests/binder_test.cpp
@@ -129,6 +129,7 @@
 using android::net::RULE_PRIORITY_UID_DEFAULT_UNREACHABLE;
 using android::net::RULE_PRIORITY_UID_EXPLICIT_NETWORK;
 using android::net::RULE_PRIORITY_UID_IMPLICIT_NETWORK;
+using android::net::RULE_PRIORITY_UID_LOCAL_ROUTES;
 using android::net::RULE_PRIORITY_VPN_FALLTHROUGH;
 using android::net::SockDiag;
 using android::net::TetherOffloadRuleParcel;
@@ -225,7 +226,8 @@
                                      unique_fd* acceptedSocket);
 
     void createVpnNetworkWithUid(bool secure, uid_t uid, int vpnNetId = TEST_NETID2,
-                                 int fallthroughNetId = TEST_NETID1);
+                                 int fallthroughNetId = TEST_NETID1,
+                                 int nonDefaultNetId = TEST_NETID3);
 
     void createAndSetDefaultNetwork(int netId, const std::string& interface,
                                     int permission = INetd::PERMISSION_NONE);
@@ -1625,6 +1627,121 @@
 
 }  // namespace
 
+TEST_F(NetdBinderTest, NetworkAddRemoveRouteToLocalExcludeTable) {
+    static const struct {
+        const char* ipVersion;
+        const char* testDest;
+        const char* testNextHop;
+        const bool expectInLocalTable;
+    } kTestData[] = {{IP_RULE_V6, "::/0", "fe80::", false},
+                     {IP_RULE_V6, "::/0", "", false},
+                     {IP_RULE_V6, "2001:db8:cafe::/64", "fe80::", false},
+                     {IP_RULE_V6, "fe80::/64", "", true},
+                     {IP_RULE_V6, "2001:db8:cafe::/48", "", true},
+                     {IP_RULE_V6, "2001:db8:cafe::/64", "unreachable", false},
+                     {IP_RULE_V6, "2001:db8:ca00::/40", "", true},
+                     {IP_RULE_V4, "0.0.0.0/0", "10.251.10.1", false},
+                     {IP_RULE_V4, "192.1.0.0/16", "", false},
+                     {IP_RULE_V4, "192.168.0.0/15", "", false},
+                     {IP_RULE_V4, "192.168.0.0/16", "", true},
+                     {IP_RULE_V4, "192.168.0.0/24", "", true},
+                     {IP_RULE_V4, "100.1.0.0/16", "", false},
+                     {IP_RULE_V4, "100.0.0.0/8", "", false},
+                     {IP_RULE_V4, "100.64.0.0/10", "", true},
+                     {IP_RULE_V4, "100.64.0.0/16", "", true},
+                     {IP_RULE_V4, "100.64.0.0/10", "throw", false},
+                     {IP_RULE_V4, "172.0.0.0/8", "", false},
+                     {IP_RULE_V4, "172.16.0.0/12", "", true},
+                     {IP_RULE_V4, "172.16.0.0/16", "", true},
+                     {IP_RULE_V4, "172.16.0.0/12", "unreachable", false},
+                     {IP_RULE_V4, "172.32.0.0/12", "", false},
+                     {IP_RULE_V4, "169.0.0.0/8", "", false},
+                     {IP_RULE_V4, "169.254.0.0/16", "", true},
+                     {IP_RULE_V4, "169.254.0.0/20", "", true},
+                     {IP_RULE_V4, "169.254.3.0/24", "", true},
+                     {IP_RULE_V4, "170.254.0.0/16", "", false},
+                     {IP_RULE_V4, "10.0.0.0/8", "", true},
+                     {IP_RULE_V4, "10.0.0.0/7", "", false},
+                     {IP_RULE_V4, "10.0.0.0/16", "", true},
+                     {IP_RULE_V4, "10.251.0.0/16", "", true},
+                     {IP_RULE_V4, "10.251.250.0/24", "", true},
+                     {IP_RULE_V4, "10.251.10.2/31", "throw", false},
+                     {IP_RULE_V4, "10.251.10.2/31", "unreachable", false}};
+
+    // To ensure that the nexthops for the above are reachable.
+    // Otherwise, the routes can't be created.
+    static const struct {
+        const char* ipVersion;
+        const char* testDest;
+        const char* testNextHop;
+    } kDirectlyConnectedRoutes[] = {
+            {IP_RULE_V4, "10.251.10.0/30", ""},
+            {IP_RULE_V6, "2001:db8::/32", ""},
+    };
+
+    // Add test physical network
+    const auto& config = makeNativeNetworkConfig(TEST_NETID1, NativeNetworkType::PHYSICAL,
+                                                 INetd::PERMISSION_NONE, false, false);
+    EXPECT_TRUE(mNetd->networkCreate(config).isOk());
+    EXPECT_TRUE(mNetd->networkAddInterface(TEST_NETID1, sTun.name()).isOk());
+
+    // Get current default network NetId
+    binder::Status status = mNetd->networkGetDefault(&mStoredDefaultNetwork);
+    ASSERT_TRUE(status.isOk()) << status.exceptionMessage();
+
+    // Set default network
+    EXPECT_TRUE(mNetd->networkSetDefault(TEST_NETID1).isOk());
+
+    std::string localTableName = std::string(sTun.name() + "_local");
+    // Set up link-local routes for connectivity to the "gateway"
+    for (size_t i = 0; i < std::size(kDirectlyConnectedRoutes); i++) {
+        const auto& td = kDirectlyConnectedRoutes[i];
+
+        binder::Status status =
+                mNetd->networkAddRoute(TEST_NETID1, sTun.name(), td.testDest, td.testNextHop);
+        EXPECT_TRUE(status.isOk()) << status.exceptionMessage();
+        expectNetworkRouteExists(td.ipVersion, sTun.name(), td.testDest, td.testNextHop,
+                                 sTun.name().c_str());
+        // Verify routes in local table
+        expectNetworkRouteExists(td.ipVersion, sTun.name(), td.testDest, td.testNextHop,
+                                 localTableName.c_str());
+    }
+
+    for (size_t i = 0; i < std::size(kTestData); i++) {
+        const auto& td = kTestData[i];
+        SCOPED_TRACE(StringPrintf("case ip:%s, dest:%s, nexHop:%s, expect:%d", td.ipVersion,
+                                  td.testDest, td.testNextHop, td.expectInLocalTable));
+        binder::Status status =
+                mNetd->networkAddRoute(TEST_NETID1, sTun.name(), td.testDest, td.testNextHop);
+        EXPECT_TRUE(status.isOk()) << status.exceptionMessage();
+        // Verify routes in local table
+        if (td.expectInLocalTable) {
+            expectNetworkRouteExists(td.ipVersion, sTun.name(), td.testDest, td.testNextHop,
+                                     localTableName.c_str());
+        } else {
+            expectNetworkRouteDoesNotExist(td.ipVersion, sTun.name(), td.testDest, td.testNextHop,
+                                           localTableName.c_str());
+        }
+
+        status = mNetd->networkRemoveRoute(TEST_NETID1, sTun.name(), td.testDest, td.testNextHop);
+        EXPECT_TRUE(status.isOk()) << status.exceptionMessage();
+        expectNetworkRouteDoesNotExist(td.ipVersion, sTun.name(), td.testDest, td.testNextHop,
+                                       localTableName.c_str());
+    }
+
+    for (size_t i = 0; i < std::size(kDirectlyConnectedRoutes); i++) {
+        const auto& td = kDirectlyConnectedRoutes[i];
+        status = mNetd->networkRemoveRoute(TEST_NETID1, sTun.name(), td.testDest, td.testNextHop);
+        EXPECT_TRUE(status.isOk()) << status.exceptionMessage();
+    }
+
+    // Set default network back
+    status = mNetd->networkSetDefault(mStoredDefaultNetwork);
+
+    // Remove test physical network
+    EXPECT_TRUE(mNetd->networkDestroy(TEST_NETID1).isOk());
+}
+
 namespace {
 
 bool getIpfwdV4Enable() {
@@ -3272,18 +3389,26 @@
 }
 
 void NetdBinderTest::createVpnNetworkWithUid(bool secure, uid_t uid, int vpnNetId,
-                                             int fallthroughNetId) {
+                                             int fallthroughNetId, int nonDefaultNetId) {
     // Re-init sTun* to ensure route rule exists.
     sTun.destroy();
     sTun.init();
     sTun2.destroy();
     sTun2.init();
+    sTun3.destroy();
+    sTun3.init();
 
     // Create physical network with fallthroughNetId but not set it as default network
     auto config = makeNativeNetworkConfig(fallthroughNetId, NativeNetworkType::PHYSICAL,
                                           INetd::PERMISSION_NONE, false, false);
     EXPECT_TRUE(mNetd->networkCreate(config).isOk());
     EXPECT_TRUE(mNetd->networkAddInterface(fallthroughNetId, sTun.name()).isOk());
+    // Create another physical network in order to test VPN behaviour with multiple networks
+    // connected, of which one may be the default.
+    auto nonDefaultNetworkConfig = makeNativeNetworkConfig(
+            nonDefaultNetId, NativeNetworkType::PHYSICAL, INetd::PERMISSION_NONE, false, false);
+    EXPECT_TRUE(mNetd->networkCreate(nonDefaultNetworkConfig).isOk());
+    EXPECT_TRUE(mNetd->networkAddInterface(nonDefaultNetId, sTun3.name()).isOk());
 
     // Create VPN with vpnNetId
     config.netId = vpnNetId;
@@ -3299,6 +3424,9 @@
     EXPECT_TRUE(mNetd->networkAddRoute(TEST_NETID1, sTun.name(), "::/0", "").isOk());
     // Add limited route
     EXPECT_TRUE(mNetd->networkAddRoute(TEST_NETID2, sTun2.name(), "2001:db8::/32", "").isOk());
+
+    // Also add default route to non-default network for per app default use.
+    EXPECT_TRUE(mNetd->networkAddRoute(TEST_NETID3, sTun3.name(), "::/0", "").isOk());
 }
 
 void NetdBinderTest::createAndSetDefaultNetwork(int netId, const std::string& interface,
@@ -3463,9 +3591,10 @@
 }
 
 void expectVpnFallthroughWorks(android::net::INetd* netdService, bool bypassable, uid_t uid,
-                               const TunInterface& fallthroughNetwork,
-                               const TunInterface& vpnNetwork, int vpnNetId = TEST_NETID2,
-                               int fallthroughNetId = TEST_NETID1) {
+                               uid_t uidNotInVpn, const TunInterface& fallthroughNetwork,
+                               const TunInterface& vpnNetwork, const TunInterface& otherNetwork,
+                               int vpnNetId = TEST_NETID2, int fallthroughNetId = TEST_NETID1,
+                               int otherNetId = TEST_NETID3) {
     // Set default network to NETID_UNSET
     EXPECT_TRUE(netdService->networkSetDefault(NETID_UNSET).isOk());
 
@@ -3499,8 +3628,10 @@
     // Check if fallthrough rule exists
     expectVpnFallthroughRuleExists(fallthroughNetwork.name(), vpnNetId);
 
-    // Check if local exclusion rule exists
+    // Check if local exclusion rule exists for default network
     expectVpnLocalExclusionRuleExists(fallthroughNetwork.name(), true);
+    // No local exclusion rule for non-default network
+    expectVpnLocalExclusionRuleExists(otherNetwork.name(), false);
 
     // Expect fallthrough to default network
     // The fwmark differs depending on whether the VPN is bypassable or not.
@@ -3540,6 +3671,25 @@
         EXPECT_FALSE(sendIPv6PacketFromUid(uid, outsideVpnAddr, &fwmark, fallthroughFd));
         EXPECT_FALSE(sendIPv6PacketFromUid(uid, insideVpnAddr, &fwmark, fallthroughFd));
     }
+
+    // Add per-app uid ranges.
+    EXPECT_TRUE(netdService
+                        ->networkAddUidRanges(otherNetId,
+                                              {makeUidRangeParcel(uidNotInVpn, uidNotInVpn)})
+                        .isOk());
+
+    int appDefaultFd = otherNetwork.getFdForTesting();
+
+    // UID is not inside the VPN range, so it won't go to vpn network.
+    // It won't fall into per app local rule because it's explicitly selected.
+    EXPECT_TRUE(sendIPv6PacketFromUid(uidNotInVpn, outsideVpnAddr, &fwmark, fallthroughFd));
+    EXPECT_TRUE(sendIPv6PacketFromUid(uidNotInVpn, insideVpnAddr, &fwmark, fallthroughFd));
+
+    // Reset explicitly selection.
+    setNetworkForProcess(NETID_UNSET);
+    // Connections can go to app default network.
+    EXPECT_TRUE(sendIPv6PacketFromUid(uidNotInVpn, insideVpnAddr, &fwmark, appDefaultFd));
+    EXPECT_TRUE(sendIPv6PacketFromUid(uidNotInVpn, outsideVpnAddr, &fwmark, appDefaultFd));
 }
 
 }  // namespace
@@ -3548,14 +3698,16 @@
     createVpnNetworkWithUid(true /* secure */, TEST_UID1);
     // Get current default network NetId
     ASSERT_TRUE(mNetd->networkGetDefault(&mStoredDefaultNetwork).isOk());
-    expectVpnFallthroughWorks(mNetd.get(), false /* bypassable */, TEST_UID1, sTun, sTun2);
+    expectVpnFallthroughWorks(mNetd.get(), false /* bypassable */, TEST_UID1, TEST_UID2, sTun,
+                              sTun2, sTun3);
 }
 
 TEST_F(NetdBinderTest, BypassableVPNFallthrough) {
     createVpnNetworkWithUid(false /* secure */, TEST_UID1);
     // Get current default network NetId
     ASSERT_TRUE(mNetd->networkGetDefault(&mStoredDefaultNetwork).isOk());
-    expectVpnFallthroughWorks(mNetd.get(), true /* bypassable */, TEST_UID1, sTun, sTun2);
+    expectVpnFallthroughWorks(mNetd.get(), true /* bypassable */, TEST_UID1, TEST_UID2, sTun, sTun2,
+                              sTun3);
 }
 
 namespace {
@@ -4013,6 +4165,7 @@
     ASSERT_EQ(expectedResults.size(), uidRanges.size());
     if (iface.size()) {
         std::string action = StringPrintf("lookup %s ", iface.c_str());
+        std::string action_local = StringPrintf("lookup %s_local ", iface.c_str());
         for (unsigned long i = 0; i < uidRanges.size(); i++) {
             EXPECT_EQ(expectedResults[i],
                       ipRuleExistsForRange(RULE_PRIORITY_UID_EXPLICIT_NETWORK + subPriority,
@@ -4023,6 +4176,8 @@
             EXPECT_EQ(expectedResults[i],
                       ipRuleExistsForRange(RULE_PRIORITY_UID_DEFAULT_NETWORK + subPriority,
                                            uidRanges[i], action));
+            EXPECT_EQ(expectedResults[i], ipRuleExistsForRange(RULE_PRIORITY_UID_LOCAL_ROUTES,
+                                                               uidRanges[i], action_local));
         }
     } else {
         std::string action = "unreachable";