Snap for 6630229 from 2ad382e0e00a9f49dd513870c7bac0575dc6abf3 to rvc-release

Change-Id: I6b5068f6224208052954d9278cbcfaddc8ca0dc0
diff --git a/src/android/net/ip/IpClient.java b/src/android/net/ip/IpClient.java
index 1591f43..c4f46ae 100644
--- a/src/android/net/ip/IpClient.java
+++ b/src/android/net/ip/IpClient.java
@@ -389,6 +389,9 @@
     private static final int CMD_COMPLETE_PRECONNECTION = 16;
     private static final int CMD_UPDATE_L2INFORMATION = 17;
 
+    private static final int ARG_LINKPROP_CHANGED_LINKSTATE_DOWN = 0;
+    private static final int ARG_LINKPROP_CHANGED_LINKSTATE_UP = 1;
+
     // Internal commands to use instead of trying to call transitionTo() inside
     // a given State's enter() method. Calling transitionTo() from enter/exit
     // encounters a Log.wtf() that can cause trouble on eng builds.
@@ -596,7 +599,9 @@
         mLinkObserver = new IpClientLinkObserver(
                 mContext, getHandler(),
                 mInterfaceName,
-                () -> sendMessage(EVENT_NETLINK_LINKPROPERTIES_CHANGED),
+                (ifaceUp) -> sendMessage(EVENT_NETLINK_LINKPROPERTIES_CHANGED, ifaceUp
+                        ? ARG_LINKPROP_CHANGED_LINKSTATE_UP
+                        : ARG_LINKPROP_CHANGED_LINKSTATE_DOWN),
                 config, mLog) {
             @Override
             public void onInterfaceAdded(String iface) {
@@ -2053,12 +2058,14 @@
 
                 case EVENT_NETLINK_LINKPROPERTIES_CHANGED:
                     // EVENT_NETLINK_LINKPROPERTIES_CHANGED message will be received in both of
-                    // provisioning loss and normal user termination case (e.g. turn off wifi or
-                    // switch to another wifi ssid), hence, checking current interface change
-                    // status (down or up) would help distinguish.
-                    final boolean ifUp = (msg.arg1 != 0);
+                    // provisioning loss and normal user termination cases (e.g. turn off wifi or
+                    // switch to another wifi ssid), hence, checking the current interface link
+                    // state (down or up) helps distinguish the two cases: if the link state is
+                    // down, provisioning is only lost because the link is being torn down (for
+                    // example when turning off wifi), so treat it as a normal termination.
                     if (!handleLinkPropertiesUpdate(SEND_CALLBACKS)) {
-                        transitionToStoppingState(ifUp ? DisconnectCode.DC_PROVISIONING_FAIL
+                        final boolean linkStateUp = (msg.arg1 == ARG_LINKPROP_CHANGED_LINKSTATE_UP);
+                        transitionToStoppingState(linkStateUp ? DisconnectCode.DC_PROVISIONING_FAIL
                                 : DisconnectCode.DC_NORMAL_TERMINATION);
                     }
                     break;
diff --git a/src/android/net/ip/IpClientLinkObserver.java b/src/android/net/ip/IpClientLinkObserver.java
index dcbca94..82f8d5d 100644
--- a/src/android/net/ip/IpClientLinkObserver.java
+++ b/src/android/net/ip/IpClientLinkObserver.java
@@ -89,8 +89,13 @@
     public interface Callback {
         /**
          * Called when some properties of the link were updated.
+         *
+         * @param linkState Whether the interface link state is up as per the latest
+         *                  {@link #onInterfaceLinkStateChanged(String, boolean)} callback. This
+         *                  should only be used for metrics purposes, as it could be inconsistent
+         *                  with {@link #getLinkProperties()} in particular.
          */
-        void update();
+        void update(boolean linkState);
     }
 
     /** Configuration parameters for IpClientLinkObserver. */
@@ -105,6 +110,7 @@
     private final String mInterfaceName;
     private final Callback mCallback;
     private final LinkProperties mLinkProperties;
+    private boolean mInterfaceLinkState;
     private DnsServerRepository mDnsServerRepository;
     private final AlarmManager mAlarmManager;
     private final Configuration mConfig;
@@ -121,6 +127,7 @@
         mLinkProperties = new LinkProperties();
         mLinkProperties.setInterfaceName(mInterfaceName);
         mConfig = config;
+        mInterfaceLinkState = true; // Assume up by default
         mDnsServerRepository = new DnsServerRepository(config.minRdnssLifetime);
         mAlarmManager = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
         mNetlinkMonitor = new MyNetlinkMonitor(h, log, mTag);
@@ -148,8 +155,22 @@
             // now empty. Note that from the moment that the interface is removed, any further
             // interface-specific messages (e.g., RTM_DELADDR) will not reach us, because the netd
             // code that parses them will not be able to resolve the ifindex to an interface name.
-            clearLinkProperties();
-            mCallback.update();
+            final boolean linkState;
+            synchronized (this) {
+                clearLinkProperties();
+                linkState = getInterfaceLinkStateLocked();
+            }
+            mCallback.update(linkState);
+        }
+    }
+
+    @Override
+    public void onInterfaceLinkStateChanged(String iface, boolean state) {
+        if (mInterfaceName.equals(iface)) {
+            maybeLog("interfaceLinkStateChanged", iface + (state ? " up" : " down"));
+            synchronized (this) {
+                setInterfaceLinkStateLocked(state);
+            }
         }
     }
 
@@ -157,12 +178,14 @@
     public void onInterfaceAddressUpdated(LinkAddress address, String iface) {
         if (mInterfaceName.equals(iface)) {
             maybeLog("addressUpdated", iface, address);
-            boolean changed;
+            final boolean changed;
+            final boolean linkState;
             synchronized (this) {
                 changed = mLinkProperties.addLinkAddress(address);
+                linkState = getInterfaceLinkStateLocked();
             }
             if (changed) {
-                mCallback.update();
+                mCallback.update(linkState);
             }
         }
     }
@@ -171,12 +194,14 @@
     public void onInterfaceAddressRemoved(LinkAddress address, String iface) {
         if (mInterfaceName.equals(iface)) {
             maybeLog("addressRemoved", iface, address);
-            boolean changed;
+            final boolean changed;
+            final boolean linkState;
             synchronized (this) {
                 changed = mLinkProperties.removeLinkAddress(address);
+                linkState = getInterfaceLinkStateLocked();
             }
             if (changed) {
-                mCallback.update();
+                mCallback.update(linkState);
             }
         }
     }
@@ -185,12 +210,14 @@
     public void onRouteUpdated(RouteInfo route) {
         if (mInterfaceName.equals(route.getInterface())) {
             maybeLog("routeUpdated", route);
-            boolean changed;
+            final boolean changed;
+            final boolean linkState;
             synchronized (this) {
                 changed = mLinkProperties.addRoute(route);
+                linkState = getInterfaceLinkStateLocked();
             }
             if (changed) {
-                mCallback.update();
+                mCallback.update(linkState);
             }
         }
     }
@@ -199,12 +226,14 @@
     public void onRouteRemoved(RouteInfo route) {
         if (mInterfaceName.equals(route.getInterface())) {
             maybeLog("routeRemoved", route);
-            boolean changed;
+            final boolean changed;
+            final boolean linkState;
             synchronized (this) {
                 changed = mLinkProperties.removeRoute(route);
+                linkState = getInterfaceLinkStateLocked();
             }
             if (changed) {
-                mCallback.update();
+                mCallback.update(linkState);
             }
         }
     }
@@ -213,12 +242,14 @@
     public void onInterfaceDnsServerInfo(String iface, long lifetime, String[] addresses) {
         if (mInterfaceName.equals(iface)) {
             maybeLog("interfaceDnsServerInfo", Arrays.toString(addresses));
-            boolean changed = mDnsServerRepository.addServers(lifetime, addresses);
+            final boolean changed = mDnsServerRepository.addServers(lifetime, addresses);
+            final boolean linkState;
             if (changed) {
                 synchronized (this) {
                     mDnsServerRepository.setDnsServersOn(mLinkProperties);
+                    linkState = getInterfaceLinkStateLocked();
                 }
-                mCallback.update();
+                mCallback.update(linkState);
             }
         }
     }
@@ -243,6 +274,14 @@
         mLinkProperties.setInterfaceName(mInterfaceName);
     }
 
+    private boolean getInterfaceLinkStateLocked() {
+        return mInterfaceLinkState;
+    }
+
+    private void setInterfaceLinkStateLocked(boolean state) {
+        mInterfaceLinkState = state;
+    }
+
     /** Notifies this object of new interface parameters. */
     public void setInterfaceParams(InterfaceParams params) {
         mNetlinkMonitor.setIfindex(params.index);
@@ -355,7 +394,7 @@
                 cancelPref64Alarm();
             }
 
-            mCallback.update();
+            mCallback.update(getInterfaceLinkStateLocked());
         }
 
         private void processPref64Option(StructNdOptPref64 opt, final long now) {