Merge "Delete unused default DNS setting code" into nyc-dev
diff --git a/packages/SettingsLib/res/values-be-rBY/strings.xml b/packages/SettingsLib/res/values-be-rBY/strings.xml
index ebb5a59..a70fc69 100644
--- a/packages/SettingsLib/res/values-be-rBY/strings.xml
+++ b/packages/SettingsLib/res/values-be-rBY/strings.xml
@@ -278,7 +278,7 @@
     <string name="enable_webview_multiprocess_desc" msgid="852226124223847283">"Запусціць апрацоўшчыкі WebView у ізаляваным працэсе."</string>
     <string name="select_webview_provider_title" msgid="4628592979751918907">"Рэалізацыя WebView"</string>
     <string name="select_webview_provider_dialog_title" msgid="4370551378720004872">"Наладзіць рэалізацыю WebView"</string>
-    <string name="select_webview_provider_toast_text" msgid="5466970498308266359">"Гэты элемент больш не даступны для выбару. Паспрабуйце яшчэ раз."</string>
+    <string name="select_webview_provider_toast_text" msgid="5466970498308266359">"Гэты варыянт больш не даступны. Паспрабуйце яшчэ раз."</string>
     <string name="convert_to_file_encryption" msgid="3060156730651061223">"Перайсці на шыфраванне файлаў"</string>
     <string name="convert_to_file_encryption_enabled" msgid="2861258671151428346">"Пераход..."</string>
     <string name="convert_to_file_encryption_done" msgid="7859766358000523953">"Шыфраванне файлаў ужо дзейнічае"</string>
diff --git a/services/net/java/android/net/apf/ApfFilter.java b/services/net/java/android/net/apf/ApfFilter.java
index e1aca97..f430a30 100644
--- a/services/net/java/android/net/apf/ApfFilter.java
+++ b/services/net/java/android/net/apf/ApfFilter.java
@@ -18,6 +18,7 @@
 
 import static android.system.OsConstants.*;
 
+import android.net.LinkProperties;
 import android.net.NetworkUtils;
 import android.net.apf.ApfGenerator;
 import android.net.apf.ApfGenerator.IllegalInstructionException;
@@ -136,6 +137,16 @@
     // NOTE: this must be added to the IPv4 header length in IPV4_HEADER_SIZE_MEMORY_SLOT
     private static final int DHCP_CLIENT_MAC_OFFSET = ETH_HEADER_LEN + UDP_HEADER_LEN + 28;
 
+    private static int ARP_HEADER_OFFSET = ETH_HEADER_LEN;
+    private static final byte[] ARP_IPV4_REQUEST_HEADER = new byte[]{
+            0, 1, // Hardware type: Ethernet (1)
+            8, 0, // Protocol type: IP (0x0800)
+            6,    // Hardware size: 6
+            4,    // Protocol size: 4
+            0, 1  // Opcode: request (1)
+    };
+    private static int ARP_TARGET_IP_ADDRESS_OFFSET = ETH_HEADER_LEN + 24;
+
     private final ApfCapabilities mApfCapabilities;
     private final IpManager.Callback mIpManagerCallback;
     private final NetworkInterface mNetworkInterface;
@@ -145,6 +156,9 @@
     private long mUniqueCounter;
     @GuardedBy("this")
     private boolean mMulticastFilter;
+    // Our IPv4 address, if we have just one, otherwise null.
+    @GuardedBy("this")
+    private byte[] mIPv4Address;
 
     private ApfFilter(ApfCapabilities apfCapabilities, NetworkInterface networkInterface,
             IpManager.Callback ipManagerCallback, boolean multicastFilter) {
@@ -507,6 +521,33 @@
     private byte[] mLastInstalledProgram;
 
     /**
+     * Generate filter code to process ARP packets. Execution of this code ends in either the
+     * DROP_LABEL or PASS_LABEL and does not fall off the end.
+     * Preconditions:
+     *  - Packet being filtered is ARP
+     */
+    @GuardedBy("this")
+    private void generateArpFilterLocked(ApfGenerator gen) throws IllegalInstructionException {
+        // Here's a basic summary of what the ARP filter program does:
+        //
+        // if it's not an ARP IPv4 request:
+        //   pass
+        // if it's not a request for our IPv4 address:
+        //   drop
+        // pass
+
+        // if it's not an ARP IPv4 request, pass
+        gen.addLoadImmediate(Register.R0, ARP_HEADER_OFFSET);
+        gen.addJumpIfBytesNotEqual(Register.R0, ARP_IPV4_REQUEST_HEADER, gen.PASS_LABEL);
+        // if it's not a request for our IPv4 address, drop
+        gen.addLoadImmediate(Register.R0, ARP_TARGET_IP_ADDRESS_OFFSET);
+        gen.addJumpIfBytesNotEqual(Register.R0, mIPv4Address, gen.DROP_LABEL);
+
+        // Otherwise, pass
+        gen.addJump(gen.PASS_LABEL);
+    }
+
+    /**
      * Generate filter code to process IPv4 packets. Execution of this code ends in either the
      * DROP_LABEL or PASS_LABEL and does not fall off the end.
      * Preconditions:
@@ -563,7 +604,6 @@
      * DROP_LABEL or PASS_LABEL, or falls off the end for ICMPv6 packets.
      * Preconditions:
      *  - Packet being filtered is IPv6
-     *  - R1 is initialized to 0
      */
     @GuardedBy("this")
     private void generateIPv6FilterLocked(ApfGenerator gen) throws IllegalInstructionException {
@@ -595,6 +635,7 @@
     /**
      * Begin generating an APF program to:
      * <ul>
+     * <li>Drop ARP requests not for us, if mIPv4Address is set,
      * <li>Drop IPv4 broadcast packets, except DHCP destined to our MAC,
      * <li>Drop IPv4 multicast packets, if mMulticastFilter,
      * <li>Pass all other IPv4 packets,
@@ -613,16 +654,31 @@
 
         // Here's a basic summary of what the initial program does:
         //
+        // if it's ARP:
+        //   inesrt ARP filter to drop or pass these appropriately
         // if it's IPv4:
         //   insert IPv4 filter to drop or pass these appropriately
         // if it's not IPv6:
         //   pass
         // insert IPv6 filter to drop, pass, or fall off the end for ICMPv6 packets
 
+        gen.addLoad16(Register.R0, ETH_ETHERTYPE_OFFSET);
+
+        if (mIPv4Address != null) {
+            // Add ARP filters:
+            String skipArpFiltersLabel = "skipArpFilters";
+            // If not ARP, skip ARP filters
+            // NOTE: Relies on R0 containing ethertype.
+            gen.addJumpIfR0NotEquals(ETH_P_ARP, skipArpFiltersLabel);
+            generateArpFilterLocked(gen);
+            gen.defineLabel(skipArpFiltersLabel);
+        }
+
         // Add IPv4 filters:
         String skipIPv4FiltersLabel = "skipIPv4Filters";
-        // If not IPv4, skip IPv4 filters
-        gen.addLoad16(Register.R0, ETH_ETHERTYPE_OFFSET);
+        // NOTE: Relies on R0 containing ethertype. This is safe because if we got here, we did not
+        // execute the ARP filter, since that filter does not fall through, but either drops or
+        // passes.
         gen.addJumpIfR0NotEquals(ETH_P_IP, skipIPv4FiltersLabel);
         // NOTE: Relies on R1 being initialized to 0.
         generateIPv4FilterLocked(gen);
@@ -631,8 +687,8 @@
         // Add IPv6 filters:
         // If not IPv6, pass
         // NOTE: Relies on R0 containing ethertype. This is safe because if we got here, we did not
-        // execute the IPv4 filter, since that filter does not fall through, but either drops or
-        // passes.
+        // execute the ARP or IPv4 filters, since those filters do not fall through, but either
+        // drop or pass.
         gen.addJumpIfR0NotEquals(ETH_P_IPV6, gen.PASS_LABEL);
         generateIPv6FilterLocked(gen);
         return gen;
@@ -785,10 +841,36 @@
         }
     }
 
+    // Find the single IPv4 address if there is one, otherwise return null.
+    private static byte[] findIPv4Address(LinkProperties lp) {
+        byte[] ipv4Address = null;
+        for (InetAddress inetAddr : lp.getAddresses()) {
+            byte[] addr = inetAddr.getAddress();
+            if (addr.length != 4) continue;
+            // More than one IPv4 address, abort
+            if (ipv4Address != null && !Arrays.equals(ipv4Address, addr)) return null;
+            ipv4Address = addr;
+        }
+        return ipv4Address;
+    }
+
+    public synchronized void setLinkProperties(LinkProperties lp) {
+        // NOTE: Do not keep a copy of LinkProperties as it would further duplicate state.
+        byte[] ipv4Address = findIPv4Address(lp);
+        // If ipv4Address is the same as mIPv4Address, then there's no change, just return.
+        if (Arrays.equals(ipv4Address, mIPv4Address)) return;
+        // Otherwise update mIPv4Address and install new program.
+        mIPv4Address = ipv4Address;
+        installNewProgramLocked();
+    }
+
     public synchronized void dump(IndentingPrintWriter pw) {
-        pw.println("APF version: " + mApfCapabilities.apfVersionSupported);
-        pw.println("Max program size: " + mApfCapabilities.maximumApfProgramSize);
+        pw.println("APF caps: " + mApfCapabilities);
         pw.println("Receive thread: " + (mReceiveThread != null ? "RUNNING" : "STOPPED"));
+        pw.println("Multicast filtering: " + mMulticastFilter);
+        try {
+            pw.println("IPv4 address: " + InetAddress.getByAddress(mIPv4Address));
+        } catch (UnknownHostException|NullPointerException e) {}
         if (mLastTimeInstalledProgram == 0) {
             pw.println("No program installed.");
             return;
diff --git a/services/net/java/android/net/ip/IpManager.java b/services/net/java/android/net/ip/IpManager.java
index ece6a1e..4a83c6f 100644
--- a/services/net/java/android/net/ip/IpManager.java
+++ b/services/net/java/android/net/ip/IpManager.java
@@ -536,6 +536,7 @@
     }
 
     private void dispatchCallback(ProvisioningChange delta, LinkProperties newLp) {
+        if (mApfFilter != null) mApfFilter.setLinkProperties(newLp);
         switch (delta) {
             case GAINED_PROVISIONING:
                 if (VDBG) { Log.d(mTag, "onProvisioningSuccess()"); }