Merge "Fixed NullPointerException in IpSecService-Netd binder layer"
diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java
index 5c11100..25c96d1 100644
--- a/services/core/java/com/android/server/ConnectivityService.java
+++ b/services/core/java/com/android/server/ConnectivityService.java
@@ -71,7 +71,6 @@
 import android.net.RouteInfo;
 import android.net.UidRange;
 import android.net.Uri;
-import android.net.metrics.DefaultNetworkEvent;
 import android.net.metrics.IpConnectivityLog;
 import android.net.metrics.NetworkEvent;
 import android.net.util.MultinetworkPolicyTracker;
@@ -129,6 +128,7 @@
 import com.android.server.LocalServices;
 import com.android.server.am.BatteryStatsService;
 import com.android.server.connectivity.DataConnectionStats;
+import com.android.server.connectivity.IpConnectivityMetrics;
 import com.android.server.connectivity.KeepaliveTracker;
 import com.android.server.connectivity.LingerMonitor;
 import com.android.server.connectivity.MockableSystemProperties;
@@ -2283,7 +2283,7 @@
                 // Let rematchAllNetworksAndRequests() below record a new default network event
                 // if there is a fallback. Taken together, the two form a X -> 0, 0 -> Y sequence
                 // whose timestamps tell how long it takes to recover a default network.
-                logDefaultNetworkEvent(null, nai);
+                metricsLogger().defaultNetworkMetrics().logDefaultNetworkEvent(null, nai);
             }
             notifyIfacesChangedForNetworkStats();
             // TODO - we shouldn't send CALLBACK_LOST to requests that can be satisfied
@@ -5012,7 +5012,8 @@
             // Notify system services that this network is up.
             makeDefault(newNetwork);
             // Log 0 -> X and Y -> X default network transitions, where X is the new default.
-            logDefaultNetworkEvent(newNetwork, oldDefaultNetwork);
+            metricsLogger().defaultNetworkMetrics().logDefaultNetworkEvent(
+                    newNetwork, oldDefaultNetwork);
             // Have a new default network, release the transition wakelock in
             scheduleReleaseNetworkTransitionWakelock();
         }
@@ -5571,25 +5572,10 @@
         return ServiceManager.checkService(name) != null;
     }
 
-    private void logDefaultNetworkEvent(NetworkAgentInfo newNai, NetworkAgentInfo prevNai) {
-        int newNetid = NETID_UNSET;
-        int prevNetid = NETID_UNSET;
-        int[] transports = new int[0];
-        boolean hadIPv4 = false;
-        boolean hadIPv6 = false;
-
-        if (newNai != null) {
-            newNetid = newNai.network.netId;
-            transports = newNai.networkCapabilities.getTransportTypes();
-        }
-        if (prevNai != null) {
-            prevNetid = prevNai.network.netId;
-            final LinkProperties lp = prevNai.linkProperties;
-            hadIPv4 = lp.hasIPv4Address() && lp.hasIPv4DefaultRoute();
-            hadIPv6 = lp.hasGlobalIPv6Address() && lp.hasIPv6DefaultRoute();
-        }
-
-        mMetricsLog.log(new DefaultNetworkEvent(newNetid, transports, prevNetid, hadIPv4, hadIPv6));
+    @VisibleForTesting
+    protected IpConnectivityMetrics.Logger metricsLogger() {
+        return checkNotNull(LocalServices.getService(IpConnectivityMetrics.Logger.class),
+                "no IpConnectivityMetrics service");
     }
 
     private void logNetworkEvent(NetworkAgentInfo nai, int evtype) {
diff --git a/tests/net/java/com/android/server/ConnectivityServiceTest.java b/tests/net/java/com/android/server/ConnectivityServiceTest.java
index c2cb66d..27a29b6 100644
--- a/tests/net/java/com/android/server/ConnectivityServiceTest.java
+++ b/tests/net/java/com/android/server/ConnectivityServiceTest.java
@@ -104,6 +104,8 @@
 import com.android.internal.util.WakeupMessage;
 import com.android.internal.util.test.BroadcastInterceptingContext;
 import com.android.internal.util.test.FakeSettingsProvider;
+import com.android.server.connectivity.DefaultNetworkMetrics;
+import com.android.server.connectivity.IpConnectivityMetrics;
 import com.android.server.connectivity.MockableSystemProperties;
 import com.android.server.connectivity.NetworkAgentInfo;
 import com.android.server.connectivity.NetworkMonitor;
@@ -156,6 +158,9 @@
     private MockNetworkAgent mEthernetNetworkAgent;
     private Context mContext;
 
+    @Mock IpConnectivityMetrics.Logger mMetricsService;
+    @Mock DefaultNetworkMetrics mDefaultNetworkMetrics;
+
     // This class exists to test bindProcessToNetwork and getBoundNetworkForProcess. These methods
     // do not go through ConnectivityService but talk to netd directly, so they don't automatically
     // reflect the state of our test ConnectivityService.
@@ -805,6 +810,11 @@
             return Context.ETHERNET_SERVICE.equals(name);
         }
 
+        @Override
+        protected IpConnectivityMetrics.Logger metricsLogger() {
+            return mMetricsService;
+        }
+
         public WrappedNetworkMonitor getLastCreatedWrappedNetworkMonitor() {
             return mLastCreatedNetworkMonitor;
         }
@@ -833,6 +843,9 @@
     public void setUp() throws Exception {
         mContext = InstrumentationRegistry.getContext();
 
+        MockitoAnnotations.initMocks(this);
+        when(mMetricsService.defaultNetworkMetrics()).thenReturn(mDefaultNetworkMetrics);
+
         // InstrumentationTestRunner prepares a looper, but AndroidJUnitRunner does not.
         // http://b/25897652 .
         if (Looper.myLooper() == null) {
diff --git a/tests/net/java/com/android/server/connectivity/IpConnectivityEventBuilderTest.java b/tests/net/java/com/android/server/connectivity/IpConnectivityEventBuilderTest.java
index 2624176..ad6ebf9 100644
--- a/tests/net/java/com/android/server/connectivity/IpConnectivityEventBuilderTest.java
+++ b/tests/net/java/com/android/server/connectivity/IpConnectivityEventBuilderTest.java
@@ -198,21 +198,20 @@
 
     @Test
     public void testDefaultNetworkEventSerialization() {
-        ConnectivityMetricsEvent ev = describeIpEvent(
-                aType(DefaultNetworkEvent.class),
-                anInt(102),
-                anIntArray(1, 2, 3),
-                anInt(101),
-                aBool(true),
-                aBool(false));
+        DefaultNetworkEvent ev = new DefaultNetworkEvent();
+        ev.netId = 102;
+        ev.prevNetId = 101;
+        ev.transportTypes = new int[]{1, 2, 3};
+        ev.prevIPv4 = true;
+        ev.prevIPv6 = true;
 
         String want = String.join("\n",
                 "dropped_events: 0",
                 "events <",
                 "  if_name: \"\"",
                 "  link_layer: 0",
-                "  network_id: 0",
-                "  time_ms: 1",
+                "  network_id: 102",
+                "  time_ms: 0",
                 "  transports: 0",
                 "  default_network_event <",
                 "    default_network_duration_ms: 0",
@@ -226,7 +225,7 @@
                 "    previous_network_id <",
                 "      network_id: 101",
                 "    >",
-                "    previous_network_ip_support: 1",
+                "    previous_network_ip_support: 3",
                 "    transport_types: 1",
                 "    transport_types: 2",
                 "    transport_types: 3",
@@ -234,7 +233,7 @@
                 ">",
                 "version: 2\n");
 
-        verifySerialization(want, ev);
+        verifySerialization(want, IpConnectivityEventBuilder.toProto(ev));
     }
 
     @Test
diff --git a/tests/net/java/com/android/server/connectivity/IpConnectivityMetricsTest.java b/tests/net/java/com/android/server/connectivity/IpConnectivityMetricsTest.java
index a395c48..6c1decc 100644
--- a/tests/net/java/com/android/server/connectivity/IpConnectivityMetricsTest.java
+++ b/tests/net/java/com/android/server/connectivity/IpConnectivityMetricsTest.java
@@ -18,6 +18,7 @@
 
 import static android.net.metrics.INetdEventListener.EVENT_GETADDRINFO;
 import static android.net.metrics.INetdEventListener.EVENT_GETHOSTBYNAME;
+import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.timeout;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
@@ -30,6 +31,10 @@
 import android.net.ConnectivityManager;
 import android.net.ConnectivityMetricsEvent;
 import android.net.IIpConnectivityMetrics;
+import android.net.IpPrefix;
+import android.net.LinkAddress;
+import android.net.LinkProperties;
+import android.net.RouteInfo;
 import android.net.Network;
 import android.net.NetworkCapabilities;
 import android.net.metrics.ApfProgramEvent;
@@ -41,18 +46,22 @@
 import android.net.metrics.IpReachabilityEvent;
 import android.net.metrics.RaEvent;
 import android.net.metrics.ValidationProbeEvent;
-import android.system.OsConstants;
 import android.os.Parcelable;
 import android.support.test.runner.AndroidJUnit4;
+import android.system.OsConstants;
 import android.test.suitebuilder.annotation.SmallTest;
 import android.util.Base64;
+
+import com.android.internal.util.BitUtils;
 import com.android.server.connectivity.metrics.nano.IpConnectivityLogClass;
+
 import java.io.PrintWriter;
 import java.io.StringWriter;
 import java.util.Collections;
 import java.util.Comparator;
 import java.util.Iterator;
 import java.util.List;
+
 import org.mockito.ArgumentCaptor;
 import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
@@ -162,6 +171,144 @@
     }
 
     @Test
+    public void testDefaultNetworkEvents() throws Exception {
+        final long cell = BitUtils.packBits(new int[]{NetworkCapabilities.TRANSPORT_CELLULAR});
+        final long wifi = BitUtils.packBits(new int[]{NetworkCapabilities.TRANSPORT_WIFI});
+
+        NetworkAgentInfo[][] defaultNetworks = {
+            // nothing -> cell
+            {null, makeNai(100, 10, false, true, cell)},
+            // cell -> wifi
+            {makeNai(100, 50, true, true, cell), makeNai(101, 20, true, false, wifi)},
+            // wifi -> nothing
+            {makeNai(101, 60, true, false, wifi), null},
+            // nothing -> cell
+            {null, makeNai(102, 10, true, true, cell)},
+            // cell -> wifi
+            {makeNai(102, 50, true, true, cell), makeNai(103, 20, true, false, wifi)},
+        };
+
+        for (NetworkAgentInfo[] pair : defaultNetworks) {
+            mService.mDefaultNetworkMetrics.logDefaultNetworkEvent(pair[1], pair[0]);
+        }
+
+        String want = String.join("\n",
+                "dropped_events: 0",
+                "events <",
+                "  if_name: \"\"",
+                "  link_layer: 0",
+                "  network_id: 100",
+                "  time_ms: 0",
+                "  transports: 0",
+                "  default_network_event <",
+                "    default_network_duration_ms: 0",
+                "    final_score: 0",
+                "    initial_score: 0",
+                "    ip_support: 0",
+                "    network_id <",
+                "      network_id: 100",
+                "    >",
+                "    no_default_network_duration_ms: 0",
+                "    previous_network_id <",
+                "      network_id: 0",
+                "    >",
+                "    previous_network_ip_support: 0",
+                "    transport_types: 0",
+                "  >",
+                ">",
+                "events <",
+                "  if_name: \"\"",
+                "  link_layer: 0",
+                "  network_id: 101",
+                "  time_ms: 0",
+                "  transports: 0",
+                "  default_network_event <",
+                "    default_network_duration_ms: 0",
+                "    final_score: 0",
+                "    initial_score: 0",
+                "    ip_support: 0",
+                "    network_id <",
+                "      network_id: 101",
+                "    >",
+                "    no_default_network_duration_ms: 0",
+                "    previous_network_id <",
+                "      network_id: 100",
+                "    >",
+                "    previous_network_ip_support: 3",
+                "    transport_types: 1",
+                "  >",
+                ">",
+                "events <",
+                "  if_name: \"\"",
+                "  link_layer: 0",
+                "  network_id: 0",
+                "  time_ms: 0",
+                "  transports: 0",
+                "  default_network_event <",
+                "    default_network_duration_ms: 0",
+                "    final_score: 0",
+                "    initial_score: 0",
+                "    ip_support: 0",
+                "    network_id <",
+                "      network_id: 0",
+                "    >",
+                "    no_default_network_duration_ms: 0",
+                "    previous_network_id <",
+                "      network_id: 101",
+                "    >",
+                "    previous_network_ip_support: 1",
+                "  >",
+                ">",
+                "events <",
+                "  if_name: \"\"",
+                "  link_layer: 0",
+                "  network_id: 102",
+                "  time_ms: 0",
+                "  transports: 0",
+                "  default_network_event <",
+                "    default_network_duration_ms: 0",
+                "    final_score: 0",
+                "    initial_score: 0",
+                "    ip_support: 0",
+                "    network_id <",
+                "      network_id: 102",
+                "    >",
+                "    no_default_network_duration_ms: 0",
+                "    previous_network_id <",
+                "      network_id: 0",
+                "    >",
+                "    previous_network_ip_support: 0",
+                "    transport_types: 0",
+                "  >",
+                ">",
+                "events <",
+                "  if_name: \"\"",
+                "  link_layer: 0",
+                "  network_id: 103",
+                "  time_ms: 0",
+                "  transports: 0",
+                "  default_network_event <",
+                "    default_network_duration_ms: 0",
+                "    final_score: 0",
+                "    initial_score: 0",
+                "    ip_support: 0",
+                "    network_id <",
+                "      network_id: 103",
+                "    >",
+                "    no_default_network_duration_ms: 0",
+                "    previous_network_id <",
+                "      network_id: 102",
+                "    >",
+                "    previous_network_ip_support: 3",
+                "    transport_types: 1",
+                "  >",
+                ">",
+                "version: 2\n");
+
+        verifySerialization(want, getdump("flush"));
+    }
+
+    @Test
     public void testEndToEndLogging() throws Exception {
         // TODO: instead of comparing textpb to textpb, parse textpb and compare proto to proto.
         IpConnectivityLog logger = new IpConnectivityLog(mService.impl);
@@ -194,7 +341,6 @@
         Parcelable[] events = {
             new IpReachabilityEvent(IpReachabilityEvent.NUD_FAILED),
             new DhcpClientEvent("SomeState", 192),
-            new DefaultNetworkEvent(102, new int[]{1,2,3}, 101, true, false),
             new IpManagerEvent(IpManagerEvent.PROVISIONING_OK, 5678),
             validationEv,
             apfStats,
@@ -233,6 +379,13 @@
         wakeupEvent("wlan0", 10008);
         wakeupEvent("rmnet0", 1000);
 
+        final long cell = BitUtils.packBits(new int[]{NetworkCapabilities.TRANSPORT_CELLULAR});
+        final long wifi = BitUtils.packBits(new int[]{NetworkCapabilities.TRANSPORT_WIFI});
+        NetworkAgentInfo cellNai = makeNai(100, 50, false, true, cell);
+        NetworkAgentInfo wifiNai = makeNai(101, 60, true, false, wifi);
+        mService.mDefaultNetworkMetrics.logDefaultNetworkEvent(cellNai, null);
+        mService.mDefaultNetworkMetrics.logDefaultNetworkEvent(wifiNai, cellNai);
+
         String want = String.join("\n",
                 "dropped_events: 0",
                 "events <",
@@ -264,30 +417,6 @@
                 "  network_id: 0",
                 "  time_ms: 300",
                 "  transports: 0",
-                "  default_network_event <",
-                "    default_network_duration_ms: 0",
-                "    final_score: 0",
-                "    initial_score: 0",
-                "    ip_support: 0",
-                "    network_id <",
-                "      network_id: 102",
-                "    >",
-                "    no_default_network_duration_ms: 0",
-                "    previous_network_id <",
-                "      network_id: 101",
-                "    >",
-                "    previous_network_ip_support: 1",
-                "    transport_types: 1",
-                "    transport_types: 2",
-                "    transport_types: 3",
-                "  >",
-                ">",
-                "events <",
-                "  if_name: \"\"",
-                "  link_layer: 4",
-                "  network_id: 0",
-                "  time_ms: 400",
-                "  transports: 0",
                 "  ip_provisioning_event <",
                 "    event_type: 1",
                 "    if_name: \"\"",
@@ -298,7 +427,7 @@
                 "  if_name: \"\"",
                 "  link_layer: 4",
                 "  network_id: 0",
-                "  time_ms: 500",
+                "  time_ms: 400",
                 "  transports: 0",
                 "  validation_probe_event <",
                 "    latency_ms: 40730",
@@ -310,7 +439,7 @@
                 "  if_name: \"\"",
                 "  link_layer: 4",
                 "  network_id: 0",
-                "  time_ms: 600",
+                "  time_ms: 500",
                 "  transports: 0",
                 "  apf_statistics <",
                 "    dropped_ras: 2",
@@ -331,7 +460,7 @@
                 "  if_name: \"\"",
                 "  link_layer: 4",
                 "  network_id: 0",
-                "  time_ms: 700",
+                "  time_ms: 600",
                 "  transports: 0",
                 "  ra_event <",
                 "    dnssl_lifetime: -1",
@@ -344,6 +473,50 @@
                 ">",
                 "events <",
                 "  if_name: \"\"",
+                "  link_layer: 0",
+                "  network_id: 100",
+                "  time_ms: 0",
+                "  transports: 0",
+                "  default_network_event <",
+                "    default_network_duration_ms: 0",
+                "    final_score: 0",
+                "    initial_score: 0",
+                "    ip_support: 0",
+                "    network_id <",
+                "      network_id: 100",
+                "    >",
+                "    no_default_network_duration_ms: 0",
+                "    previous_network_id <",
+                "      network_id: 0",
+                "    >",
+                "    previous_network_ip_support: 0",
+                "    transport_types: 0",
+                "  >",
+                ">",
+                "events <",
+                "  if_name: \"\"",
+                "  link_layer: 0",
+                "  network_id: 101",
+                "  time_ms: 0",
+                "  transports: 0",
+                "  default_network_event <",
+                "    default_network_duration_ms: 0",
+                "    final_score: 0",
+                "    initial_score: 0",
+                "    ip_support: 0",
+                "    network_id <",
+                "      network_id: 101",
+                "    >",
+                "    no_default_network_duration_ms: 0",
+                "    previous_network_id <",
+                "      network_id: 100",
+                "    >",
+                "    previous_network_ip_support: 2",
+                "    transport_types: 1",
+                "  >",
+                ">",
+                "events <",
+                "  if_name: \"\"",
                 "  link_layer: 4",
                 "  network_id: 100",
                 "  time_ms: 0",
@@ -471,6 +644,26 @@
         mNetdListener.onWakeupEvent(prefix, uid, uid, 0);
     }
 
+    NetworkAgentInfo makeNai(int netId, int score, boolean ipv4, boolean ipv6, long transports) {
+        NetworkAgentInfo nai = mock(NetworkAgentInfo.class);
+        when(nai.network()).thenReturn(new Network(netId));
+        when(nai.getCurrentScore()).thenReturn(score);
+        nai.linkProperties = new LinkProperties();
+        nai.networkCapabilities = new NetworkCapabilities();
+        for (int t : BitUtils.unpackBits(transports)) {
+            nai.networkCapabilities.addTransportType(t);
+        }
+        if (ipv4) {
+            nai.linkProperties.addLinkAddress(new LinkAddress("192.0.2.12/24"));
+            nai.linkProperties.addRoute(new RouteInfo(new IpPrefix("0.0.0.0/0")));
+        }
+        if (ipv6) {
+            nai.linkProperties.addLinkAddress(new LinkAddress("2001:db8:dead:beef:f00::a0/64"));
+            nai.linkProperties.addRoute(new RouteInfo(new IpPrefix("::/0")));
+        }
+        return nai;
+    }
+
     List<ConnectivityMetricsEvent> verifyEvents(int n, int timeoutMs) throws Exception {
         ArgumentCaptor<ConnectivityMetricsEvent> captor =
                 ArgumentCaptor.forClass(ConnectivityMetricsEvent.class);