Merge "[automerger skipped] Merge "Fix doc of NETWORK_TYPE_NR & add converter" am: 1abe6aa53f am: a4e9cec4b3 am: 8a5c3bc6c2 -s ours am skip reason: change_id Ib9ee0dbe0234ef1bf9ef22900b00ddd47195914f with SHA1 3955593f7e is in history"
diff --git a/Android.bp b/Android.bp
index 8a2089f..644cf0e 100644
--- a/Android.bp
+++ b/Android.bp
@@ -637,6 +637,7 @@
         "wifi/java/android/net/wifi/ISoftApCallback.aidl",
         "wifi/java/android/net/wifi/ITrafficStateCallback.aidl",
         "wifi/java/android/net/wifi/IWifiManager.aidl",
+        "wifi/java/android/net/wifi/IWifiUsabilityStatsListener.aidl",
         "wifi/java/android/net/wifi/aware/IWifiAwareDiscoverySessionCallback.aidl",
         "wifi/java/android/net/wifi/aware/IWifiAwareEventCallback.aidl",
         "wifi/java/android/net/wifi/aware/IWifiAwareMacAddressProvider.aidl",
diff --git a/api/system-current.txt b/api/system-current.txt
index 730cac6..2e8f69f 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -4610,6 +4610,7 @@
   }
 
   public class WifiManager {
+    method @RequiresPermission("android.permission.WIFI_UPDATE_USABILITY_STATS_SCORE") public void addWifiUsabilityStatsListener(@NonNull java.util.concurrent.Executor, @NonNull android.net.wifi.WifiManager.WifiUsabilityStatsListener);
     method @RequiresPermission(anyOf={"android.permission.NETWORK_SETTINGS", android.Manifest.permission.NETWORK_SETUP_WIZARD, "android.permission.NETWORK_STACK"}) public void connect(android.net.wifi.WifiConfiguration, android.net.wifi.WifiManager.ActionListener);
     method @RequiresPermission(anyOf={"android.permission.NETWORK_SETTINGS", android.Manifest.permission.NETWORK_SETUP_WIZARD, "android.permission.NETWORK_STACK"}) public void connect(int, android.net.wifi.WifiManager.ActionListener);
     method @RequiresPermission(anyOf={"android.permission.NETWORK_SETTINGS", android.Manifest.permission.NETWORK_SETUP_WIZARD, "android.permission.NETWORK_STACK"}) public void disable(int, android.net.wifi.WifiManager.ActionListener);
@@ -4625,6 +4626,7 @@
     method @RequiresPermission(android.Manifest.permission.ACCESS_WIFI_STATE) public boolean isWifiApEnabled();
     method public boolean isWifiScannerSupported();
     method @RequiresPermission("android.permission.NETWORK_SETTINGS") public void registerNetworkRequestMatchCallback(@NonNull android.net.wifi.WifiManager.NetworkRequestMatchCallback, @Nullable android.os.Handler);
+    method @RequiresPermission("android.permission.WIFI_UPDATE_USABILITY_STATS_SCORE") public void removeWifiUsabilityStatsListener(@NonNull android.net.wifi.WifiManager.WifiUsabilityStatsListener);
     method @RequiresPermission(anyOf={"android.permission.NETWORK_SETTINGS", android.Manifest.permission.NETWORK_SETUP_WIZARD, "android.permission.NETWORK_STACK"}) public void save(android.net.wifi.WifiConfiguration, android.net.wifi.WifiManager.ActionListener);
     method @RequiresPermission("android.permission.WIFI_SET_DEVICE_MOBILITY_STATE") public void setDeviceMobilityState(int);
     method @RequiresPermission(android.Manifest.permission.CHANGE_WIFI_STATE) public boolean setWifiApConfiguration(android.net.wifi.WifiConfiguration);
@@ -4682,6 +4684,10 @@
     method public void select(@NonNull android.net.wifi.WifiConfiguration);
   }
 
+  public static interface WifiManager.WifiUsabilityStatsListener {
+    method public void onStatsUpdated(int, boolean, android.net.wifi.WifiUsabilityStatsEntry);
+  }
+
   public class WifiNetworkConnectionStatistics implements android.os.Parcelable {
     ctor public WifiNetworkConnectionStatistics(int, int);
     ctor public WifiNetworkConnectionStatistics();
@@ -4811,6 +4817,31 @@
     field @Deprecated public int unchangedSampleSize;
   }
 
+  public final class WifiUsabilityStatsEntry implements android.os.Parcelable {
+    method public int describeContents();
+    method public void writeToParcel(android.os.Parcel, int);
+    field public static final android.os.Parcelable.Creator<android.net.wifi.WifiUsabilityStatsEntry> CREATOR;
+    field public final int linkSpeedMbps;
+    field public final int rssi;
+    field public final long timeStampMs;
+    field public final long totalBackgroundScanTimeMs;
+    field public final long totalBeaconRx;
+    field public final long totalCcaBusyFreqTimeMs;
+    field public final long totalHotspot2ScanTimeMs;
+    field public final long totalNanScanTimeMs;
+    field public final long totalPnoScanTimeMs;
+    field public final long totalRadioOnFreqTimeMs;
+    field public final long totalRadioOnTimeMs;
+    field public final long totalRadioRxTimeMs;
+    field public final long totalRadioTxTimeMs;
+    field public final long totalRoamScanTimeMs;
+    field public final long totalRxSuccess;
+    field public final long totalScanTimeMs;
+    field public final long totalTxBad;
+    field public final long totalTxRetries;
+    field public final long totalTxSuccess;
+  }
+
 }
 
 package android.net.wifi.aware {
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index c7cba62..f92df6a 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -1654,6 +1654,11 @@
     <permission android:name="android.permission.WIFI_SET_DEVICE_MOBILITY_STATE"
         android:protectionLevel="signature|privileged" />
 
+    <!-- #SystemApi @hide Allows privileged system APK to update Wifi usability stats and score.
+         <p>Not for use by third-party applications. -->
+    <permission android:name="android.permission.WIFI_UPDATE_USABILITY_STATS_SCORE"
+        android:protectionLevel="signature|privileged" />
+
     <!-- ======================================= -->
     <!-- Permissions for short range, peripheral networks -->
     <!-- ======================================= -->
diff --git a/proto/src/wifi.proto b/proto/src/wifi.proto
index 79b63bc..6656919 100644
--- a/proto/src/wifi.proto
+++ b/proto/src/wifi.proto
@@ -1800,6 +1800,17 @@
 
   // Sequence number from external system app to framework
   optional int32 seq_num_to_framework = 19;
+
+  // The total time CCA is on busy status on the current frequency in ms
+  // counted from the last radio chip reset
+  optional int64 total_cca_busy_freq_time_ms = 20;
+
+  // The total radio on time of the current frequency from the last radio
+  // chip reset
+  optional int64 total_radio_on_freq_time_ms = 21;
+
+  // The total number of beacons received from the last radio chip reset
+  optional int64 total_beacon_rx = 22;
 }
 
 message WifiUsabilityStats {
diff --git a/wifi/java/android/net/wifi/IWifiManager.aidl b/wifi/java/android/net/wifi/IWifiManager.aidl
index 46c4191..a0ce9dd 100644
--- a/wifi/java/android/net/wifi/IWifiManager.aidl
+++ b/wifi/java/android/net/wifi/IWifiManager.aidl
@@ -28,6 +28,7 @@
 import android.net.wifi.INetworkRequestMatchCallback;
 import android.net.wifi.ISoftApCallback;
 import android.net.wifi.ITrafficStateCallback;
+import android.net.wifi.IWifiUsabilityStatsListener;
 import android.net.wifi.PasspointManagementObjectDefinition;
 import android.net.wifi.ScanResult;
 import android.net.wifi.WifiActivityEnergyInfo;
@@ -186,6 +187,10 @@
 
     void unregisterSoftApCallback(int callbackIdentifier);
 
+    void addWifiUsabilityStatsListener(in IBinder binder, in IWifiUsabilityStatsListener listener, int listenerIdentifier);
+
+    void removeWifiUsabilityStatsListener(int listenerIdentifier);
+
     void registerTrafficStateCallback(in IBinder binder, in ITrafficStateCallback callback, int callbackIdentifier);
 
     void unregisterTrafficStateCallback(int callbackIdentifier);
@@ -210,4 +215,3 @@
 
     void stopDppSession();
 }
-
diff --git a/wifi/java/android/net/wifi/IWifiUsabilityStatsListener.aidl b/wifi/java/android/net/wifi/IWifiUsabilityStatsListener.aidl
new file mode 100644
index 0000000..284ffaa
--- /dev/null
+++ b/wifi/java/android/net/wifi/IWifiUsabilityStatsListener.aidl
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.net.wifi;
+
+import android.net.wifi.WifiUsabilityStatsEntry;
+
+/**
+ * Interface for Wi-Fi usability stats listener.
+ *
+ * @hide
+ */
+oneway interface IWifiUsabilityStatsListener
+{
+    /**
+     * Service to manager callback providing current Wi-Fi usability stats.
+     *
+     * @param seqNum The sequence number of stats, used to derive the timing of updated Wi-Fi
+     *               usability statistics, set by framework and shall be incremented by one
+     *               after each update.
+     * @param isSameBssidAndFreq The flag to indicate whether the BSSID and the frequency of
+     *                           network stays the same or not relative to the last update of
+     *                           Wi-Fi usability stats.
+     * @param stats The updated Wi-Fi usability statistics.
+     */
+    void onStatsUpdated(int seqNum, boolean isSameBssidAndFreq,
+            in WifiUsabilityStatsEntry stats);
+}
diff --git a/wifi/java/android/net/wifi/WifiManager.java b/wifi/java/android/net/wifi/WifiManager.java
index 1fd45e7..084bd09 100644
--- a/wifi/java/android/net/wifi/WifiManager.java
+++ b/wifi/java/android/net/wifi/WifiManager.java
@@ -4777,4 +4777,92 @@
             });
         }
     }
+
+    /**
+     * Interface for Wi-Fi usability statistics listener. Should be implemented by applications and
+     * set when calling {@link WifiManager#addWifiUsabilityStatsListener(Executor,
+     * WifiUsabilityStatsListener)}.
+     *
+     * @hide
+     */
+    @SystemApi
+    public interface WifiUsabilityStatsListener {
+        /**
+         * Called when Wi-Fi usability statistics is updated.
+         *
+         * @param seqNum The sequence number of statistics, used to derive the timing of updated
+         *               Wi-Fi usability statistics, set by framework and incremented by one after
+         *               each update.
+         * @param isSameBssidAndFreq The flag to indicate whether the BSSID and the frequency of
+         *                           network stays the same or not relative to the last update of
+         *                           Wi-Fi usability stats.
+         * @param stats The updated Wi-Fi usability statistics.
+         */
+        void onStatsUpdated(int seqNum, boolean isSameBssidAndFreq,
+                WifiUsabilityStatsEntry stats);
+    }
+
+    /**
+     * Adds a listener for Wi-Fi usability statistics. See {@link WifiUsabilityStatsListener}.
+     * Multiple listeners can be added. Callers will be invoked periodically by framework to
+     * inform clients about the current Wi-Fi usability statistics. Callers can remove a previously
+     * added listener using {@link removeWifiUsabilityStatsListener}.
+     *
+     * @param executor The executor on which callback will be invoked.
+     * @param listener Listener for Wifi usability statistics.
+     *
+     * @hide
+     */
+    @SystemApi
+    @RequiresPermission(android.Manifest.permission.WIFI_UPDATE_USABILITY_STATS_SCORE)
+    public void addWifiUsabilityStatsListener(@NonNull @CallbackExecutor Executor executor,
+            @NonNull WifiUsabilityStatsListener listener) {
+        if (executor == null) throw new IllegalArgumentException("executor cannot be null");
+        if (listener == null) throw new IllegalArgumentException("listener cannot be null");
+        if (mVerboseLoggingEnabled) {
+            Log.v(TAG, "addWifiUsabilityStatsListener: listener=" + listener);
+        }
+        try {
+            mService.addWifiUsabilityStatsListener(new Binder(),
+                    new IWifiUsabilityStatsListener.Stub() {
+                        @Override
+                        public void onStatsUpdated(int seqNum, boolean isSameBssidAndFreq,
+                                WifiUsabilityStatsEntry stats) {
+                            if (mVerboseLoggingEnabled) {
+                                Log.v(TAG, "WifiUsabilityStatsListener: onStatsUpdated: seqNum="
+                                        + seqNum);
+                            }
+                            Binder.withCleanCallingIdentity(() ->
+                                    executor.execute(() -> listener.onStatsUpdated(seqNum,
+                                            isSameBssidAndFreq, stats)));
+                        }
+                    },
+                    listener.hashCode()
+            );
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Allow callers to remove a previously registered listener. After calling this method,
+     * applications will no longer receive Wi-Fi usability statistics.
+     *
+     * @param listener Listener to remove the Wi-Fi usability statistics.
+     *
+     * @hide
+     */
+    @SystemApi
+    @RequiresPermission(android.Manifest.permission.WIFI_UPDATE_USABILITY_STATS_SCORE)
+    public void removeWifiUsabilityStatsListener(@NonNull WifiUsabilityStatsListener listener) {
+        if (listener == null) throw new IllegalArgumentException("listener cannot be null");
+        if (mVerboseLoggingEnabled) {
+            Log.v(TAG, "removeWifiUsabilityStatsListener: listener=" + listener);
+        }
+        try {
+            mService.removeWifiUsabilityStatsListener(listener.hashCode());
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
 }
diff --git a/wifi/java/android/net/wifi/WifiUsabilityStatsEntry.aidl b/wifi/java/android/net/wifi/WifiUsabilityStatsEntry.aidl
new file mode 100644
index 0000000..839af54
--- /dev/null
+++ b/wifi/java/android/net/wifi/WifiUsabilityStatsEntry.aidl
@@ -0,0 +1,19 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.net.wifi;
+
+parcelable WifiUsabilityStatsEntry;
diff --git a/wifi/java/android/net/wifi/WifiUsabilityStatsEntry.java b/wifi/java/android/net/wifi/WifiUsabilityStatsEntry.java
new file mode 100644
index 0000000..c796e29
--- /dev/null
+++ b/wifi/java/android/net/wifi/WifiUsabilityStatsEntry.java
@@ -0,0 +1,148 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.net.wifi;
+
+import android.annotation.SystemApi;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+/**
+ * This class makes a subset of
+ * com.android.server.wifi.nano.WifiMetricsProto.WifiUsabilityStatsEntry parcelable.
+ *
+ * @hide
+ */
+@SystemApi
+public final class WifiUsabilityStatsEntry implements Parcelable {
+    /** Absolute milliseconds from device boot when these stats were sampled */
+    public final long timeStampMs;
+    /** The RSSI (in dBm) at the sample time */
+    public final int rssi;
+    /** Link speed at the sample time in Mbps */
+    public final int linkSpeedMbps;
+    /** The total number of tx success counted from the last radio chip reset */
+    public final long totalTxSuccess;
+    /** The total number of MPDU data packet retries counted from the last radio chip reset */
+    public final long totalTxRetries;
+    /** The total number of tx bad counted from the last radio chip reset */
+    public final long totalTxBad;
+    /** The total number of rx success counted from the last radio chip reset */
+    public final long totalRxSuccess;
+    /** The total time the wifi radio is on in ms counted from the last radio chip reset */
+    public final long totalRadioOnTimeMs;
+    /** The total time the wifi radio is doing tx in ms counted from the last radio chip reset */
+    public final long totalRadioTxTimeMs;
+    /** The total time the wifi radio is doing rx in ms counted from the last radio chip reset */
+    public final long totalRadioRxTimeMs;
+    /** The total time spent on all types of scans in ms counted from the last radio chip reset */
+    public final long totalScanTimeMs;
+    /** The total time spent on nan scans in ms counted from the last radio chip reset */
+    public final long totalNanScanTimeMs;
+    /** The total time spent on background scans in ms counted from the last radio chip reset */
+    public final long totalBackgroundScanTimeMs;
+    /** The total time spent on roam scans in ms counted from the last radio chip reset */
+    public final long totalRoamScanTimeMs;
+    /** The total time spent on pno scans in ms counted from the last radio chip reset */
+    public final long totalPnoScanTimeMs;
+    /** The total time spent on hotspot2.0 scans and GAS exchange in ms counted from the last radio
+     * chip reset */
+    public final long totalHotspot2ScanTimeMs;
+    /** The total time CCA is on busy status on the current frequency in ms counted from the last
+     * radio chip reset */
+    public final long totalCcaBusyFreqTimeMs;
+    /** The total radio on time of the current frequency from the last radio chip reset */
+    public final long totalRadioOnFreqTimeMs;
+    /** The total number of beacons received from the last radio chip reset */
+    public final long totalBeaconRx;
+
+    /** Constructor function {@hide} */
+    public WifiUsabilityStatsEntry(long timeStampMs, int rssi,
+            int linkSpeedMbps, long totalTxSuccess, long totalTxRetries,
+            long totalTxBad, long totalRxSuccess, long totalRadioOnTimeMs,
+            long totalRadioTxTimeMs, long totalRadioRxTimeMs, long totalScanTimeMs,
+            long totalNanScanTimeMs, long totalBackgroundScanTimeMs, long totalRoamScanTimeMs,
+            long totalPnoScanTimeMs, long totalHotspot2ScanTimeMs, long totalCcaBusyFreqTimeMs,
+            long totalRadioOnFreqTimeMs, long totalBeaconRx) {
+        this.timeStampMs = timeStampMs;
+        this.rssi = rssi;
+        this.linkSpeedMbps = linkSpeedMbps;
+        this.totalTxSuccess = totalTxSuccess;
+        this.totalTxRetries = totalTxRetries;
+        this.totalTxBad = totalTxBad;
+        this.totalRxSuccess = totalRxSuccess;
+        this.totalRadioOnTimeMs = totalRadioOnTimeMs;
+        this.totalRadioTxTimeMs = totalRadioTxTimeMs;
+        this.totalRadioRxTimeMs = totalRadioRxTimeMs;
+        this.totalScanTimeMs = totalScanTimeMs;
+        this.totalNanScanTimeMs = totalNanScanTimeMs;
+        this.totalBackgroundScanTimeMs = totalBackgroundScanTimeMs;
+        this.totalRoamScanTimeMs = totalRoamScanTimeMs;
+        this.totalPnoScanTimeMs = totalPnoScanTimeMs;
+        this.totalHotspot2ScanTimeMs = totalHotspot2ScanTimeMs;
+        this.totalCcaBusyFreqTimeMs = totalCcaBusyFreqTimeMs;
+        this.totalRadioOnFreqTimeMs = totalRadioOnFreqTimeMs;
+        this.totalBeaconRx = totalBeaconRx;
+    }
+
+    /** Implement the Parcelable interface */
+    public int describeContents() {
+        return 0;
+    }
+
+    /** Implement the Parcelable interface */
+    public void writeToParcel(Parcel dest, int flags) {
+        dest.writeLong(timeStampMs);
+        dest.writeInt(rssi);
+        dest.writeInt(linkSpeedMbps);
+        dest.writeLong(totalTxSuccess);
+        dest.writeLong(totalTxRetries);
+        dest.writeLong(totalTxBad);
+        dest.writeLong(totalRxSuccess);
+        dest.writeLong(totalRadioOnTimeMs);
+        dest.writeLong(totalRadioTxTimeMs);
+        dest.writeLong(totalRadioRxTimeMs);
+        dest.writeLong(totalScanTimeMs);
+        dest.writeLong(totalNanScanTimeMs);
+        dest.writeLong(totalBackgroundScanTimeMs);
+        dest.writeLong(totalRoamScanTimeMs);
+        dest.writeLong(totalPnoScanTimeMs);
+        dest.writeLong(totalHotspot2ScanTimeMs);
+        dest.writeLong(totalCcaBusyFreqTimeMs);
+        dest.writeLong(totalRadioOnFreqTimeMs);
+        dest.writeLong(totalBeaconRx);
+    }
+
+    /** Implement the Parcelable interface */
+    public static final Creator<WifiUsabilityStatsEntry> CREATOR =
+            new Creator<WifiUsabilityStatsEntry>() {
+        public WifiUsabilityStatsEntry createFromParcel(Parcel in) {
+            return new WifiUsabilityStatsEntry(
+                    in.readLong(), in.readInt(),
+                    in.readInt(), in.readLong(), in.readLong(),
+                    in.readLong(), in.readLong(), in.readLong(),
+                    in.readLong(), in.readLong(), in.readLong(),
+                    in.readLong(), in.readLong(), in.readLong(),
+                    in.readLong(), in.readLong(), in.readLong(),
+                    in.readLong(), in.readLong()
+            );
+        }
+
+        public WifiUsabilityStatsEntry[] newArray(int size) {
+            return new WifiUsabilityStatsEntry[size];
+        }
+    };
+}
diff --git a/wifi/java/com/android/server/wifi/BaseWifiService.java b/wifi/java/com/android/server/wifi/BaseWifiService.java
index b3ac9f1..226c7c48 100644
--- a/wifi/java/com/android/server/wifi/BaseWifiService.java
+++ b/wifi/java/com/android/server/wifi/BaseWifiService.java
@@ -26,6 +26,7 @@
 import android.net.wifi.ISoftApCallback;
 import android.net.wifi.ITrafficStateCallback;
 import android.net.wifi.IWifiManager;
+import android.net.wifi.IWifiUsabilityStatsListener;
 import android.net.wifi.ScanResult;
 import android.net.wifi.WifiActivityEnergyInfo;
 import android.net.wifi.WifiConfiguration;
@@ -464,4 +465,15 @@
     public void stopDppSession() throws RemoteException {
         throw new UnsupportedOperationException();
     }
+
+    @Override
+    public void addWifiUsabilityStatsListener(
+            IBinder binder, IWifiUsabilityStatsListener listener, int listenerIdentifier) {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public void removeWifiUsabilityStatsListener(int listenerIdentifier) {
+        throw new UnsupportedOperationException();
+    }
 }
diff --git a/wifi/tests/src/android/net/wifi/WifiManagerTest.java b/wifi/tests/src/android/net/wifi/WifiManagerTest.java
index 4fbef5a..5c2f626 100644
--- a/wifi/tests/src/android/net/wifi/WifiManagerTest.java
+++ b/wifi/tests/src/android/net/wifi/WifiManagerTest.java
@@ -61,6 +61,7 @@
 import android.net.wifi.WifiManager.NetworkRequestUserSelectionCallback;
 import android.net.wifi.WifiManager.SoftApCallback;
 import android.net.wifi.WifiManager.TrafficStateCallback;
+import android.net.wifi.WifiManager.WifiUsabilityStatsListener;
 import android.os.Handler;
 import android.os.IBinder;
 import android.os.Message;
@@ -80,6 +81,7 @@
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
+import java.util.concurrent.Executor;
 
 /**
  * Unit tests for {@link android.net.wifi.WifiManager}.
@@ -103,7 +105,9 @@
     @Mock SoftApCallback mSoftApCallback;
     @Mock TrafficStateCallback mTrafficStateCallback;
     @Mock NetworkRequestMatchCallback mNetworkRequestMatchCallback;
+    @Mock WifiUsabilityStatsListener mWifiUsabilityStatsListener;
 
+    private Executor mExecutor;
     private Handler mHandler;
     private TestLooper mLooper;
     private WifiManager mWifiManager;
@@ -1342,4 +1346,40 @@
         assertArrayEquals(TEST_MAC_ADDRESSES, mWifiManager.getFactoryMacAddresses());
         verify(mWifiService).getFactoryMacAddresses();
     }
+
+    /**
+     * Verify the call to addWifiUsabilityStatsListener goes to WifiServiceImpl.
+     */
+    @Test
+    public void addWifiUsabilityStatsListeneroesToWifiServiceImpl() throws Exception {
+        mExecutor = new SynchronousExecutor();
+        mWifiManager.addWifiUsabilityStatsListener(mExecutor, mWifiUsabilityStatsListener);
+        verify(mWifiService).addWifiUsabilityStatsListener(any(IBinder.class),
+                any(IWifiUsabilityStatsListener.Stub.class), anyInt());
+    }
+
+    /**
+     * Verify the call to removeWifiUsabilityStatsListener goes to WifiServiceImpl.
+     */
+    @Test
+    public void removeWifiUsabilityListenerGoesToWifiServiceImpl() throws Exception {
+        ArgumentCaptor<Integer> listenerIdentifier = ArgumentCaptor.forClass(Integer.class);
+        mExecutor = new SynchronousExecutor();
+        mWifiManager.addWifiUsabilityStatsListener(mExecutor, mWifiUsabilityStatsListener);
+        verify(mWifiService).addWifiUsabilityStatsListener(any(IBinder.class),
+                any(IWifiUsabilityStatsListener.Stub.class), listenerIdentifier.capture());
+
+        mWifiManager.removeWifiUsabilityStatsListener(mWifiUsabilityStatsListener);
+        verify(mWifiService).removeWifiUsabilityStatsListener(
+                eq((int) listenerIdentifier.getValue()));
+    }
+
+    /**
+     * Defined for testing purpose.
+     */
+    class SynchronousExecutor implements Executor {
+        public void execute(Runnable r) {
+            r.run();
+        }
+    }
 }
diff --git a/wifi/tests/src/android/net/wifi/WifiUsabilityStatsEntryTest.java b/wifi/tests/src/android/net/wifi/WifiUsabilityStatsEntryTest.java
new file mode 100644
index 0000000..a947b55
--- /dev/null
+++ b/wifi/tests/src/android/net/wifi/WifiUsabilityStatsEntryTest.java
@@ -0,0 +1,104 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.net.wifi;
+
+import static org.junit.Assert.assertEquals;
+import static org.mockito.Mockito.validateMockitoUsage;
+
+import android.os.Parcel;
+
+import androidx.test.filters.SmallTest;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.MockitoAnnotations;
+
+
+/**
+ * Unit tests for {@link android.net.wifi.WifiUsabilityStatsEntry}.
+ */
+@SmallTest
+public class WifiUsabilityStatsEntryTest {
+
+    /**
+     * Setup before tests.
+     */
+    @Before
+    public void setUp() throws Exception {
+        MockitoAnnotations.initMocks(this);
+    }
+
+    /**
+     * Clean up after tests.
+     */
+    @After
+    public void cleanup() {
+        validateMockitoUsage();
+    }
+
+    /**
+     * Verify parcel read/write for Wifi usability stats result.
+     */
+    @Test
+    public void verifyStatsResultWriteAndThenRead() throws Exception {
+        WifiUsabilityStatsEntry writeResult = createResult();
+        WifiUsabilityStatsEntry readResult = parcelWriteRead(writeResult);
+        assertWifiUsabilityStatsEntryEquals(writeResult, readResult);
+    }
+
+    /**
+     * Write the provided {@link WifiUsabilityStatsEntry} to a parcel and deserialize it.
+     */
+    private static WifiUsabilityStatsEntry parcelWriteRead(
+            WifiUsabilityStatsEntry writeResult) throws Exception {
+        Parcel parcel = Parcel.obtain();
+        writeResult.writeToParcel(parcel, 0);
+        parcel.setDataPosition(0);    // Rewind data position back to the beginning for read.
+        return WifiUsabilityStatsEntry.CREATOR.createFromParcel(parcel);
+    }
+
+    private static WifiUsabilityStatsEntry createResult() {
+        return new WifiUsabilityStatsEntry(
+                0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18
+        );
+    }
+
+    private static void assertWifiUsabilityStatsEntryEquals(
+            WifiUsabilityStatsEntry expected,
+            WifiUsabilityStatsEntry actual) {
+        assertEquals(expected.timeStampMs, actual.timeStampMs);
+        assertEquals(expected.rssi, actual.rssi);
+        assertEquals(expected.linkSpeedMbps, actual.linkSpeedMbps);
+        assertEquals(expected.totalTxSuccess, actual.totalTxSuccess);
+        assertEquals(expected.totalTxRetries, actual.totalTxRetries);
+        assertEquals(expected.totalTxBad, actual.totalTxBad);
+        assertEquals(expected.totalRxSuccess, actual.totalRxSuccess);
+        assertEquals(expected.totalRadioOnTimeMs, actual.totalRadioOnTimeMs);
+        assertEquals(expected.totalRadioTxTimeMs, actual.totalRadioTxTimeMs);
+        assertEquals(expected.totalRadioRxTimeMs, actual.totalRadioRxTimeMs);
+        assertEquals(expected.totalScanTimeMs, actual.totalScanTimeMs);
+        assertEquals(expected.totalNanScanTimeMs, actual.totalNanScanTimeMs);
+        assertEquals(expected.totalBackgroundScanTimeMs, actual.totalBackgroundScanTimeMs);
+        assertEquals(expected.totalRoamScanTimeMs, actual.totalRoamScanTimeMs);
+        assertEquals(expected.totalPnoScanTimeMs, actual.totalPnoScanTimeMs);
+        assertEquals(expected.totalHotspot2ScanTimeMs, actual.totalHotspot2ScanTimeMs);
+        assertEquals(expected.totalCcaBusyFreqTimeMs, actual.totalCcaBusyFreqTimeMs);
+        assertEquals(expected.totalRadioOnFreqTimeMs, actual.totalRadioOnFreqTimeMs);
+        assertEquals(expected.totalBeaconRx, actual.totalBeaconRx);
+    }
+}