Merge "Add WakeupConfigStoreData for reading and writing to config store."
diff --git a/service/java/com/android/server/wifi/ExtendedWifiInfo.java b/service/java/com/android/server/wifi/ExtendedWifiInfo.java
new file mode 100644
index 0000000..5edbd34
--- /dev/null
+++ b/service/java/com/android/server/wifi/ExtendedWifiInfo.java
@@ -0,0 +1,123 @@
+/*
+ * Copyright 2017 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 com.android.server.wifi;
+
+import android.net.wifi.WifiInfo;
+
+/**
+ * Extends WifiInfo with the methods for computing the averaged packet rates
+ */
+public class ExtendedWifiInfo extends WifiInfo {
+    private static final long RESET_TIME_STAMP = Long.MIN_VALUE;
+    private static final double FILTER_TIME_CONSTANT = 3000.0;
+
+    private long mLastPacketCountUpdateTimeStamp = RESET_TIME_STAMP;
+
+    @Override
+    public void reset() {
+        super.reset();
+        mLastPacketCountUpdateTimeStamp = RESET_TIME_STAMP;
+    }
+
+    /**
+     * Updates the packet rates using link layer stats
+     *
+     * @param stats WifiLinkLayerStats
+     * @param timeStamp time in milliseconds
+     */
+    public void updatePacketRates(WifiLinkLayerStats stats, long timeStamp) {
+        if (stats != null) {
+            long txgood = stats.txmpdu_be + stats.txmpdu_bk + stats.txmpdu_vi + stats.txmpdu_vo;
+            long txretries = stats.retries_be + stats.retries_bk
+                    + stats.retries_vi + stats.retries_vo;
+            long rxgood = stats.rxmpdu_be + stats.rxmpdu_bk + stats.rxmpdu_vi + stats.rxmpdu_vo;
+            long txbad = stats.lostmpdu_be + stats.lostmpdu_bk
+                    + stats.lostmpdu_vi + stats.lostmpdu_vo;
+
+            if (mLastPacketCountUpdateTimeStamp != RESET_TIME_STAMP
+                    && mLastPacketCountUpdateTimeStamp < timeStamp
+                    && txBad <= txbad
+                    && txSuccess <= txgood
+                    && rxSuccess <= rxgood
+                    && txRetries <= txretries) {
+                long timeDelta = timeStamp - mLastPacketCountUpdateTimeStamp;
+                double lastSampleWeight = Math.exp(-1.0 * timeDelta / FILTER_TIME_CONSTANT);
+                double currentSampleWeight = 1.0 - lastSampleWeight;
+
+                txBadRate = txBadRate * lastSampleWeight
+                        + (txbad - txBad) * 1000.0 / timeDelta
+                        * currentSampleWeight;
+                txSuccessRate = txSuccessRate * lastSampleWeight
+                        + (txgood - txSuccess) * 1000.0 / timeDelta
+                        * currentSampleWeight;
+                rxSuccessRate = rxSuccessRate * lastSampleWeight
+                        + (rxgood - rxSuccess) * 1000.0 / timeDelta
+                        * currentSampleWeight;
+                txRetriesRate = txRetriesRate * lastSampleWeight
+                        + (txretries - txRetries) * 1000.0 / timeDelta
+                        * currentSampleWeight;
+            } else {
+                txBadRate = 0;
+                txSuccessRate = 0;
+                rxSuccessRate = 0;
+                txRetriesRate = 0;
+            }
+            txBad = txbad;
+            txSuccess = txgood;
+            rxSuccess = rxgood;
+            txRetries = txretries;
+            mLastPacketCountUpdateTimeStamp = timeStamp;
+        } else {
+            txBad = 0;
+            txSuccess = 0;
+            rxSuccess = 0;
+            txRetries = 0;
+            txBadRate = 0;
+            txSuccessRate = 0;
+            rxSuccessRate = 0;
+            txRetriesRate = 0;
+            mLastPacketCountUpdateTimeStamp = RESET_TIME_STAMP;
+        }
+    }
+
+    /**
+     * This function is less powerful and used if the WifiLinkLayerStats API is not implemented
+     * at the Wifi HAL
+     *
+     * @hide
+     */
+    public void updatePacketRates(long txPackets, long rxPackets) {
+        //paranoia
+        txBad = 0;
+        txRetries = 0;
+        txBadRate = 0;
+        txRetriesRate = 0;
+        if (txSuccess <= txPackets && rxSuccess <= rxPackets) {
+            txSuccessRate = (txSuccessRate * 0.5)
+                    + ((double) (txPackets - txSuccess) * 0.5);
+            rxSuccessRate = (rxSuccessRate * 0.5)
+                    + ((double) (rxPackets - rxSuccess) * 0.5);
+        } else {
+            txBadRate = 0;
+            txRetriesRate = 0;
+        }
+        txSuccess = txPackets;
+        rxSuccess = rxPackets;
+    }
+
+
+}
diff --git a/service/java/com/android/server/wifi/LegacyConnectedScore.java b/service/java/com/android/server/wifi/LegacyConnectedScore.java
index facab0a..3027696 100644
--- a/service/java/com/android/server/wifi/LegacyConnectedScore.java
+++ b/service/java/com/android/server/wifi/LegacyConnectedScore.java
@@ -63,6 +63,10 @@
     private boolean mMultiBandScanResults;
     private boolean mIsHomeNetwork;
     private int mScore = 0;
+    private int mBadRssiCount;
+    private int mLinkStuckCount;
+    private int mLowRssiCount;
+
 
     LegacyConnectedScore(Context context, WifiConfigManager wifiConfigManager, Clock clock) {
         super(clock);
@@ -109,32 +113,32 @@
             rssi += WifiConfiguration.HOME_NETWORK_RSSI_BOOST;
         }
 
-        if ((wifiInfo.txBadRate >= 1)
-                && (wifiInfo.txSuccessRate < MAX_SUCCESS_RATE_OF_STUCK_LINK)
+        if ((wifiInfo.txBadRate * 5 >= 1)
+                && (wifiInfo.txSuccessRate * 5 < MAX_SUCCESS_RATE_OF_STUCK_LINK)
                 && rssi < rssiThreshLow) {
             // Link is stuck
-            if (wifiInfo.linkStuckCount < MAX_STUCK_LINK_COUNT) {
-                wifiInfo.linkStuckCount += 1;
+            if (mLinkStuckCount < MAX_STUCK_LINK_COUNT) {
+                mLinkStuckCount += 1;
             }
-        } else if (wifiInfo.txBadRate < MIN_TX_FAILURE_RATE_FOR_WORKING_LINK) {
-            if (wifiInfo.linkStuckCount > 0) {
-                wifiInfo.linkStuckCount -= 1;
+        } else if (wifiInfo.txBadRate * 5 < MIN_TX_FAILURE_RATE_FOR_WORKING_LINK) {
+            if (mLinkStuckCount > 0) {
+                mLinkStuckCount -= 1;
             }
         }
 
         if (rssi < rssiThreshBad) {
-            if (wifiInfo.badRssiCount < MAX_BAD_RSSI_COUNT) {
-                wifiInfo.badRssiCount += 1;
+            if (mBadRssiCount < MAX_BAD_RSSI_COUNT) {
+                mBadRssiCount += 1;
             }
         } else if (rssi < rssiThreshLow) {
-            wifiInfo.lowRssiCount = MAX_LOW_RSSI_COUNT; // Dont increment the lowRssi count above 1
-            if (wifiInfo.badRssiCount > 0) {
+            mLowRssiCount = MAX_LOW_RSSI_COUNT; // Dont increment the lowRssi count above 1
+            if (mBadRssiCount > 0) {
                 // Decrement bad Rssi count
-                wifiInfo.badRssiCount -= 1;
+                mBadRssiCount -= 1;
             }
         } else {
-            wifiInfo.badRssiCount = 0;
-            wifiInfo.lowRssiCount = 0;
+            mBadRssiCount = 0;
+            mLowRssiCount = 0;
         }
 
         // Ugh, we need to finish the score calculation while we have wifiInfo
@@ -155,6 +159,9 @@
     @Override
     public void reset() {
         mScore = 0;
+        mBadRssiCount = 0;
+        mLinkStuckCount = 0;
+        mLowRssiCount = 0;
     }
 
     /**
@@ -182,18 +189,19 @@
 
         int linkSpeed = wifiInfo.getLinkSpeed();
 
-        if (wifiInfo.linkStuckCount > MIN_SUSTAINED_LINK_STUCK_COUNT) {
+        if (mLinkStuckCount > MIN_SUSTAINED_LINK_STUCK_COUNT) {
             // Once link gets stuck for more than 3 seconds, start reducing the score
-            score = score - LINK_STUCK_PENALTY * (wifiInfo.linkStuckCount - 1);
+            score = score - LINK_STUCK_PENALTY * (mLinkStuckCount - 1);
         }
 
         if (linkSpeed < linkspeedThreshBad) {
             score -= BAD_LINKSPEED_PENALTY;
-        } else if ((linkSpeed >= linkspeedThreshGood) && (wifiInfo.txSuccessRate > 5)) {
+        } else if ((linkSpeed >= linkspeedThreshGood)
+                    && (wifiInfo.txSuccessRate > 1)) {
             score += GOOD_LINKSPEED_BONUS; // So as bad rssi alone doesn't kill us
         }
 
-        score -= wifiInfo.badRssiCount * BAD_RSSI_COUNT_PENALTY + wifiInfo.lowRssiCount;
+        score -= mBadRssiCount * BAD_RSSI_COUNT_PENALTY + mLowRssiCount;
 
         if (rssi >= rssiThreshSaturated) score += 5;
 
diff --git a/service/java/com/android/server/wifi/VelocityBasedConnectedScore.java b/service/java/com/android/server/wifi/VelocityBasedConnectedScore.java
index 69643ce..38fd1ef 100644
--- a/service/java/com/android/server/wifi/VelocityBasedConnectedScore.java
+++ b/service/java/com/android/server/wifi/VelocityBasedConnectedScore.java
@@ -146,6 +146,8 @@
         return mThresholdMinimumRssi + mThresholdAdjustment;
     }
 
+    private double mMinimumPpsForMeasuringSuccess = 2.0;
+
     /**
      * Adjusts the threshold if appropriate
      * <p>
@@ -159,11 +161,12 @@
         if (mThresholdAdjustment < -7) return;
         if (mFilteredRssi >= getAdjustedRssiThreshold() + 2.0) return;
         if (Math.abs(mEstimatedRateOfRssiChange) >= 0.2) return;
-        if (wifiInfo.txSuccessRate < 10) return;
-        if (wifiInfo.rxSuccessRate < 10) return;
-        double probabilityOfSuccessfulTx = (
-                wifiInfo.txSuccessRate / (wifiInfo.txSuccessRate + wifiInfo.txBadRate)
-        );
+        double txSuccessPps = wifiInfo.txSuccessRate;
+        double rxSuccessPps = wifiInfo.rxSuccessRate;
+        if (txSuccessPps < mMinimumPpsForMeasuringSuccess) return;
+        if (rxSuccessPps < mMinimumPpsForMeasuringSuccess) return;
+        double txBadPps = wifiInfo.txBadRate;
+        double probabilityOfSuccessfulTx = txSuccessPps / (txSuccessPps + txBadPps);
         if (probabilityOfSuccessfulTx >= 0.2) {
             // May want this amount to vary with how close to threshold we are
             mThresholdAdjustment -= 0.5;
diff --git a/service/java/com/android/server/wifi/WifiLinkLayerStats.java b/service/java/com/android/server/wifi/WifiLinkLayerStats.java
new file mode 100644
index 0000000..0c58670
--- /dev/null
+++ b/service/java/com/android/server/wifi/WifiLinkLayerStats.java
@@ -0,0 +1,132 @@
+/*
+ * Copyright 2014 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 com.android.server.wifi;
+
+import java.util.Arrays;
+
+/**
+ * A class representing link layer statistics collected over a Wifi Interface.
+ */
+
+/**
+ * {@hide}
+ */
+public class WifiLinkLayerStats {
+
+    /** Number of beacons received from our own AP */
+    public int beacon_rx;
+
+    /** RSSI of management frames */
+    public int rssi_mgmt;
+
+    /* Packet counters */
+
+    /** WME Best Effort Access Category received mpdu */
+    public long rxmpdu_be;
+    /** WME Best Effort Access Category transmitted mpdu */
+    public long txmpdu_be;
+    /** WME Best Effort Access Category lost mpdu */
+    public long lostmpdu_be;
+    /** WME Best Effort Access Category number of transmission retries */
+    public long retries_be;
+
+    /** WME Background Access Category received mpdu */
+    public long rxmpdu_bk;
+    /** WME Background Access Category transmitted mpdu */
+    public long txmpdu_bk;
+    /** WME Background Access Category lost mpdu */
+    public long lostmpdu_bk;
+    /** WME Background Access Category number of transmission retries */
+    public long retries_bk;
+
+    /** WME Video Access Category received mpdu */
+    public long rxmpdu_vi;
+    /** WME Video Access Category transmitted mpdu */
+    public long txmpdu_vi;
+    /** WME Video Access Category lost mpdu */
+    public long lostmpdu_vi;
+    /** WME Video Access Category number of transmission retries */
+    public long retries_vi;
+
+    /** WME Voice Access Category received mpdu */
+    public long rxmpdu_vo;
+    /** WME Voice Access Category transmitted mpdu */
+    public long txmpdu_vo;
+    /** WME Voice Access Category lost mpdu */
+    public long lostmpdu_vo;
+    /** WME Voice Access Category number of transmission retries */
+    public long retries_vo;
+
+    /**
+     * Cumulative milliseconds when radio is awake
+     */
+    public int on_time;
+    /**
+     * Cumulative milliseconds of active transmission
+     */
+    public int tx_time;
+    /**
+     * Cumulative milliseconds per level of active transmission
+     */
+    public int[] tx_time_per_level;
+    /**
+     * Cumulative milliseconds of active receive
+     */
+    public int rx_time;
+    /**
+     * Cumulative milliseconds when radio is awake due to scan
+     */
+    public int on_time_scan;
+
+    /**
+     * TimeStamp - absolute milliseconds from boot when these stats were sampled.
+     */
+    public long timeStampInMs;
+
+    @Override
+    public String toString() {
+        StringBuilder sbuf = new StringBuilder();
+        sbuf.append(" WifiLinkLayerStats: ").append('\n');
+
+        sbuf.append(" my bss beacon rx: ").append(Integer.toString(this.beacon_rx)).append('\n');
+        sbuf.append(" RSSI mgmt: ").append(Integer.toString(this.rssi_mgmt)).append('\n');
+        sbuf.append(" BE : ").append(" rx=").append(Long.toString(this.rxmpdu_be))
+                .append(" tx=").append(Long.toString(this.txmpdu_be))
+                .append(" lost=").append(Long.toString(this.lostmpdu_be))
+                .append(" retries=").append(Long.toString(this.retries_be)).append('\n');
+        sbuf.append(" BK : ").append(" rx=").append(Long.toString(this.rxmpdu_bk))
+                .append(" tx=").append(Long.toString(this.txmpdu_bk))
+                .append(" lost=").append(Long.toString(this.lostmpdu_bk))
+                .append(" retries=").append(Long.toString(this.retries_bk)).append('\n');
+        sbuf.append(" VI : ").append(" rx=").append(Long.toString(this.rxmpdu_vi))
+                .append(" tx=").append(Long.toString(this.txmpdu_vi))
+                .append(" lost=").append(Long.toString(this.lostmpdu_vi))
+                .append(" retries=").append(Long.toString(this.retries_vi)).append('\n');
+        sbuf.append(" VO : ").append(" rx=").append(Long.toString(this.rxmpdu_vo))
+                .append(" tx=").append(Long.toString(this.txmpdu_vo))
+                .append(" lost=").append(Long.toString(this.lostmpdu_vo))
+                .append(" retries=").append(Long.toString(this.retries_vo)).append('\n');
+        sbuf.append(" on_time : ").append(Integer.toString(this.on_time))
+                .append(" rx_time=").append(Integer.toString(this.rx_time))
+                .append(" scan_time=").append(Integer.toString(this.on_time_scan)).append('\n')
+                .append(" tx_time=").append(Integer.toString(this.tx_time))
+                .append(" tx_time_per_level=" + Arrays.toString(tx_time_per_level));
+        sbuf.append(" ts=" + timeStampInMs);
+        return sbuf.toString();
+    }
+
+}
diff --git a/service/java/com/android/server/wifi/WifiNative.java b/service/java/com/android/server/wifi/WifiNative.java
index fc815f8..2f1b550 100644
--- a/service/java/com/android/server/wifi/WifiNative.java
+++ b/service/java/com/android/server/wifi/WifiNative.java
@@ -25,7 +25,6 @@
 import android.net.wifi.RttManager.ResponderConfig;
 import android.net.wifi.ScanResult;
 import android.net.wifi.WifiConfiguration;
-import android.net.wifi.WifiLinkLayerStats;
 import android.net.wifi.WifiScanner;
 import android.net.wifi.WifiWakeReasonAndCounts;
 import android.os.SystemClock;
diff --git a/service/java/com/android/server/wifi/WifiNetworkSelector.java b/service/java/com/android/server/wifi/WifiNetworkSelector.java
index 071fc07..46ded1c 100644
--- a/service/java/com/android/server/wifi/WifiNetworkSelector.java
+++ b/service/java/com/android/server/wifi/WifiNetworkSelector.java
@@ -152,9 +152,8 @@
         boolean hasQualifiedRssi =
                 (wifiInfo.is24GHz() && (currentRssi > mThresholdQualifiedRssi24))
                         || (wifiInfo.is5GHz() && (currentRssi > mThresholdQualifiedRssi5));
-        // getTxSuccessRate() and getRxSuccessRate() returns the packet rate in per 5 seconds unit.
-        boolean hasActiveStream = (wifiInfo.getTxSuccessRatePps() > mStayOnNetworkMinimumTxRate)
-                || (wifiInfo.getRxSuccessRatePps() > mStayOnNetworkMinimumRxRate);
+        boolean hasActiveStream = (wifiInfo.txSuccessRate > mStayOnNetworkMinimumTxRate)
+                || (wifiInfo.rxSuccessRate > mStayOnNetworkMinimumRxRate);
         if (hasQualifiedRssi && hasActiveStream) {
             localLog("Stay on current network because of good RSSI and ongoing traffic");
             return true;
diff --git a/service/java/com/android/server/wifi/WifiScoreReport.java b/service/java/com/android/server/wifi/WifiScoreReport.java
index e5281ef..1c258e1 100644
--- a/service/java/com/android/server/wifi/WifiScoreReport.java
+++ b/service/java/com/android/server/wifi/WifiScoreReport.java
@@ -216,7 +216,7 @@
             history = new LinkedList<>(mLinkMetricsHistory);
         }
         pw.println("time,session,rssi,filtered_rssi,rssi_threshold,"
-                + "freq,linkspeed,tx_good,tx_retry,tx_bad,rx,s0,s1,s2");
+                + "freq,linkspeed,tx_good,tx_retry,tx_bad,rx_pps,s0,s1,s2");
         for (String line : history) {
             pw.println(line);
         }
diff --git a/service/java/com/android/server/wifi/WifiServiceImpl.java b/service/java/com/android/server/wifi/WifiServiceImpl.java
index 8db180f..8e6a819 100644
--- a/service/java/com/android/server/wifi/WifiServiceImpl.java
+++ b/service/java/com/android/server/wifi/WifiServiceImpl.java
@@ -69,7 +69,6 @@
 import android.net.wifi.WifiConfiguration;
 import android.net.wifi.WifiConnectionStatistics;
 import android.net.wifi.WifiInfo;
-import android.net.wifi.WifiLinkLayerStats;
 import android.net.wifi.WifiManager;
 import android.net.wifi.WifiManager.LocalOnlyHotspotCallback;
 import android.net.wifi.WifiScanner;
diff --git a/service/java/com/android/server/wifi/WifiStateMachine.java b/service/java/com/android/server/wifi/WifiStateMachine.java
index e386654..b69d67b 100644
--- a/service/java/com/android/server/wifi/WifiStateMachine.java
+++ b/service/java/com/android/server/wifi/WifiStateMachine.java
@@ -71,7 +71,6 @@
 import android.net.wifi.WifiConnectionStatistics;
 import android.net.wifi.WifiEnterpriseConfig;
 import android.net.wifi.WifiInfo;
-import android.net.wifi.WifiLinkLayerStats;
 import android.net.wifi.WifiManager;
 import android.net.wifi.WifiScanner;
 import android.net.wifi.WifiSsid;
@@ -370,7 +369,7 @@
     private DhcpResults mDhcpResults;
 
     // NOTE: Do not return to clients - see syncRequestConnectionInfo()
-    private final WifiInfo mWifiInfo;
+    private final ExtendedWifiInfo mWifiInfo;
     private NetworkInfo mNetworkInfo;
     private final NetworkCapabilities mDfltNetworkCapabilities;
     private SupplicantStateTracker mSupplicantStateTracker;
@@ -953,7 +952,7 @@
         mWifiMonitor = mWifiInjector.getWifiMonitor();
         mWifiDiagnostics = mWifiInjector.makeWifiDiagnostics(mWifiNative);
 
-        mWifiInfo = new WifiInfo();
+        mWifiInfo = new ExtendedWifiInfo();
         mSupplicantStateTracker =
                 mFacade.makeSupplicantStateTracker(context, mWifiConfigManager, getHandler());
 
diff --git a/service/java/com/android/server/wifi/WifiVendorHal.java b/service/java/com/android/server/wifi/WifiVendorHal.java
index d552e7d..f1d2add 100644
--- a/service/java/com/android/server/wifi/WifiVendorHal.java
+++ b/service/java/com/android/server/wifi/WifiVendorHal.java
@@ -59,7 +59,6 @@
 import android.net.wifi.RttManager.ResponderConfig;
 import android.net.wifi.ScanResult;
 import android.net.wifi.WifiInfo;
-import android.net.wifi.WifiLinkLayerStats;
 import android.net.wifi.WifiManager;
 import android.net.wifi.WifiScanner;
 import android.net.wifi.WifiSsid;
@@ -868,7 +867,6 @@
     static WifiLinkLayerStats frameworkFromHalLinkLayerStats(StaLinkLayerStats stats) {
         if (stats == null) return null;
         WifiLinkLayerStats out = new WifiLinkLayerStats();
-        // unpopulated: out.status, out.SSID, out.BSSID
         out.beacon_rx = stats.iface.beaconRx;
         out.rssi_mgmt = stats.iface.avgRssiMgmt;
         // Statistics are broken out by Wireless Multimedia Extensions categories
@@ -904,7 +902,7 @@
             out.rx_time = radioStats.rxTimeInMs;
             out.on_time_scan = radioStats.onTimeInMsForScan;
         }
-        // unused: stats.timeStampInMs;
+        out.timeStampInMs = stats.timeStampInMs;
         return out;
     }
 
diff --git a/service/java/com/android/server/wifi/aware/WifiAwareNativeManager.java b/service/java/com/android/server/wifi/aware/WifiAwareNativeManager.java
index d6bec5f..f0a86bb 100644
--- a/service/java/com/android/server/wifi/aware/WifiAwareNativeManager.java
+++ b/service/java/com/android/server/wifi/aware/WifiAwareNativeManager.java
@@ -83,7 +83,7 @@
                             awareIsDown();
                         }
                     }
-                }, null);
+                }, mHandler);
         if (mHalDeviceManager.isStarted()) {
             mHalDeviceManager.registerInterfaceAvailableForRequestListener(
                     IfaceType.NAN, mInterfaceAvailableForRequestListener, mHandler);
diff --git a/tests/wifitests/src/com/android/server/wifi/VelocityBasedConnectedScoreTest.java b/tests/wifitests/src/com/android/server/wifi/VelocityBasedConnectedScoreTest.java
index 263cbed..6a72522 100644
--- a/tests/wifitests/src/com/android/server/wifi/VelocityBasedConnectedScoreTest.java
+++ b/tests/wifitests/src/com/android/server/wifi/VelocityBasedConnectedScoreTest.java
@@ -100,9 +100,9 @@
     public void allowLowRssiIfErrorRateIsLowAndSomeDataIsMoving() throws Exception {
         mWifiInfo.setRssi(mRssiExitThreshold2GHz - 2);
         mWifiInfo.setLinkSpeed(6); // Mbps
-        mWifiInfo.txSuccessRate = 10.1; // proportional to pps
+        mWifiInfo.txSuccessRate = 2.1; // proportional to pps
         mWifiInfo.txBadRate = .5;
-        mWifiInfo.rxSuccessRate = 10.1;
+        mWifiInfo.rxSuccessRate = 2.1;
         for (int i = 0; i < 10; i++) {
             mVelocityBasedConnectedScore.updateUsingWifiInfo(mWifiInfo,
                     mClock.getWallClockMillis());
diff --git a/tests/wifitests/src/com/android/server/wifi/WifiLinkLayerStatsTest.java b/tests/wifitests/src/com/android/server/wifi/WifiLinkLayerStatsTest.java
new file mode 100644
index 0000000..0189aed
--- /dev/null
+++ b/tests/wifitests/src/com/android/server/wifi/WifiLinkLayerStatsTest.java
@@ -0,0 +1,124 @@
+/*
+ * Copyright 2017 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 com.android.server.wifi;
+
+import static org.junit.Assert.assertEquals;
+
+import org.junit.Before;
+import org.junit.Test;
+
+import java.util.Random;
+
+/**
+ * Unit tests for {@link com.android.server.wifi.WifiLinkLayerStats}.
+ */
+public class WifiLinkLayerStatsTest {
+
+    ExtendedWifiInfo mWifiInfo;
+    WifiLinkLayerStats mWifiLinkLayerStats;
+    Random mRandom = new Random();
+
+    /**
+     * Sets up for unit test
+     */
+    @Before
+    public void setUp() throws Exception {
+        mWifiInfo = new ExtendedWifiInfo();
+        mWifiLinkLayerStats = new WifiLinkLayerStats();
+    }
+
+    /**
+     * Increments the counters
+     *
+     * The values are carved up among the 4 classes (be, bk, vi, vo) so the totals come out right.
+     */
+    private void bumpCounters(WifiLinkLayerStats s, int txg, int txr, int txb, int rxg) {
+        int a = mRandom.nextInt(31);
+        int b = mRandom.nextInt(31);
+        int m0 = a & b;
+        int m1 = a & ~b;
+        int m2 = ~a & b;
+        int m3 = ~a & ~b;
+        assertEquals(-1, m0 + m1 + m2 + m3);
+
+        s.rxmpdu_be += rxg & m0;
+        s.txmpdu_be += txg & m0;
+        s.lostmpdu_be += txb & m0;
+        s.retries_be += txr & m0;
+
+        s.rxmpdu_bk += rxg & m1;
+        s.txmpdu_bk += txg & m1;
+        s.lostmpdu_bk += txb & m1;
+        s.retries_bk += txr & m1;
+
+        s.rxmpdu_vi += rxg & m2;
+        s.txmpdu_vi += txg & m2;
+        s.lostmpdu_vi += txb & m2;
+        s.retries_vi += txr & m2;
+
+        s.rxmpdu_vo += rxg & m3;
+        s.txmpdu_vo += txg & m3;
+        s.lostmpdu_vo += txb & m3;
+        s.retries_vo += txr & m3;
+    }
+
+    /**
+     *
+     * Check that average rates converge to the right values
+     *
+     * Check that the total packet counts are correct
+     *
+     */
+    @Test
+    public void checkThatAverageRatesConvergeToTheRightValuesAndTotalsAreRight() throws Exception {
+        int txg = mRandom.nextInt(1000);
+        int txr = mRandom.nextInt(100);
+        int txb = mRandom.nextInt(100);
+        int rxg = mRandom.nextInt(1000);
+        int n = 3 * 5; // Time constant is 3 seconds, 5 times time constant should get 99% there
+        for (int i = 0; i < n; i++) {
+            bumpCounters(mWifiLinkLayerStats, txg, txr, txb, rxg);
+            mWifiLinkLayerStats.timeStampInMs += 1000;
+            mWifiInfo.updatePacketRates(mWifiLinkLayerStats, mWifiLinkLayerStats.timeStampInMs);
+        }
+        // assertEquals(double, double, double) takes a tolerance as the third argument
+        assertEquals((double) txg, mWifiInfo.txSuccessRate, txg * 0.02);
+        assertEquals((double) txr, mWifiInfo.txRetriesRate, txr * 0.02);
+        assertEquals((double) txb, mWifiInfo.txBadRate, txb * 0.02);
+        assertEquals((double) rxg, mWifiInfo.rxSuccessRate, rxg * 0.02);
+
+        assertEquals(mWifiInfo.txSuccess, n * txg);
+        assertEquals(mWifiInfo.txRetries, n * txr);
+        assertEquals(mWifiInfo.txBad, n * txb);
+        assertEquals(mWifiInfo.rxSuccess, n * rxg);
+    }
+
+    /**
+     * A single packet in a short period of time should have small effect
+     */
+    @Test
+    public void aSinglePacketInAShortPeriodOfTimeShouldHaveSmallEffect() throws Exception {
+        bumpCounters(mWifiLinkLayerStats, 999999999, 999999999, 999999999, 99999999);
+        mWifiLinkLayerStats.timeStampInMs = 999999999;
+        mWifiInfo.updatePacketRates(mWifiLinkLayerStats, mWifiLinkLayerStats.timeStampInMs);
+        assertEquals(0.0, mWifiInfo.txSuccessRate, 0.0001);
+        bumpCounters(mWifiLinkLayerStats, 1, 1, 1, 1);
+        mWifiLinkLayerStats.timeStampInMs += 1;
+        mWifiInfo.updatePacketRates(mWifiLinkLayerStats, mWifiLinkLayerStats.timeStampInMs);
+        assertEquals(0.33, mWifiInfo.txSuccessRate, 0.01);
+    }
+}
diff --git a/tests/wifitests/src/com/android/server/wifi/WifiNetworkSelectorTest.java b/tests/wifitests/src/com/android/server/wifi/WifiNetworkSelectorTest.java
index 3d3af36..b56da5d 100644
--- a/tests/wifitests/src/com/android/server/wifi/WifiNetworkSelectorTest.java
+++ b/tests/wifitests/src/com/android/server/wifi/WifiNetworkSelectorTest.java
@@ -613,8 +613,8 @@
         // Rssi after connected.
         when(mWifiInfo.getRssi()).thenReturn(mThresholdQualifiedRssi2G + 1);
         // No streaming traffic.
-        when(mWifiInfo.getTxSuccessRatePps()).thenReturn(0.0);
-        when(mWifiInfo.getRxSuccessRatePps()).thenReturn(0.0);
+        mWifiInfo.txSuccessRate = 0.0;
+        mWifiInfo.rxSuccessRate = 0.0;
 
         // Do not perform selection on 2GHz if current network is good and no 5GHz available
         testStayOrTryToSwitch(
@@ -640,8 +640,8 @@
         // Rssi after connected.
         when(mWifiInfo.getRssi()).thenReturn(mThresholdQualifiedRssi2G + 1);
         // No streaming traffic.
-        when(mWifiInfo.getTxSuccessRatePps()).thenReturn(0.0);
-        when(mWifiInfo.getRxSuccessRatePps()).thenReturn(0.0);
+        mWifiInfo.txSuccessRate = 0.0;
+        mWifiInfo.rxSuccessRate = 0.0;
 
         // When on 2GHz, even with "good" signal strength, run selection if 5GHz available
         testStayOrTryToSwitch(
@@ -670,8 +670,8 @@
         // Rssi after connected.
         when(mWifiInfo.getRssi()).thenReturn(mThresholdQualifiedRssi5G - 1);
         // No streaming traffic.
-        when(mWifiInfo.getTxSuccessRatePps()).thenReturn(0.0);
-        when(mWifiInfo.getRxSuccessRatePps()).thenReturn(0.0);
+        mWifiInfo.txSuccessRate = 0.0;
+        mWifiInfo.rxSuccessRate = 0.0;
 
         // Run Selection when the current 5Ghz network has low RSSI.
         testStayOrTryToSwitch(
@@ -695,8 +695,8 @@
         // Rssi after connected.
         when(mWifiInfo.getRssi()).thenReturn(mThresholdQualifiedRssi5G + 1);
         // No streaming traffic.
-        when(mWifiInfo.getTxSuccessRatePps()).thenReturn(0.0);
-        when(mWifiInfo.getRxSuccessRatePps()).thenReturn(0.0);
+        mWifiInfo.txSuccessRate = 0.0;
+        mWifiInfo.rxSuccessRate = 0.0;
 
         // Connected to a high quality 5Ghz network, so the other result is irrelevant
         testStayOrTryToSwitch(
@@ -719,8 +719,8 @@
         // Rssi after connected.
         when(mWifiInfo.getRssi()).thenReturn(mThresholdQualifiedRssi2G + 1);
         // No streaming traffic.
-        when(mWifiInfo.getTxSuccessRatePps()).thenReturn(0.0);
-        when(mWifiInfo.getRxSuccessRatePps()).thenReturn(0.0);
+        mWifiInfo.txSuccessRate = 0.0;
+        mWifiInfo.rxSuccessRate = 0.0;
 
         testStayOrTryToSwitch(
                 // Parameters for network1:
@@ -747,8 +747,8 @@
         // Rssi after connected.
         when(mWifiInfo.getRssi()).thenReturn(mThresholdQualifiedRssi2G - 1);
         // No streaming traffic.
-        when(mWifiInfo.getTxSuccessRatePps()).thenReturn(0.0);
-        when(mWifiInfo.getRxSuccessRatePps()).thenReturn(0.0);
+        mWifiInfo.txSuccessRate = 0.0;
+        mWifiInfo.rxSuccessRate = 0.0;
 
         testStayOrTryToSwitch(
                 mThresholdQualifiedRssi2G + 1 /* rssi before connected */,
@@ -769,9 +769,8 @@
         // Rssi after connected.
         when(mWifiInfo.getRssi()).thenReturn(mThresholdQualifiedRssi2G + 1);
         // Streaming traffic
-        when(mWifiInfo.getTxSuccessRatePps()).thenReturn(
-                (double) (mStayOnNetworkMinimumTxRate + 1));
-        when(mWifiInfo.getRxSuccessRatePps()).thenReturn(0.0);
+        mWifiInfo.txSuccessRate = ((double) (mStayOnNetworkMinimumTxRate + 1));
+        mWifiInfo.rxSuccessRate = 0.0;
 
         testStayOrTryToSwitch(
                 mThresholdQualifiedRssi2G + 1 /* rssi before connected */,
@@ -792,9 +791,8 @@
         // Rssi after connected.
         when(mWifiInfo.getRssi()).thenReturn(mThresholdQualifiedRssi2G + 1);
         // Streaming traffic
-        when(mWifiInfo.getTxSuccessRatePps()).thenReturn(0.0);
-        when(mWifiInfo.getRxSuccessRatePps()).thenReturn(
-                (double) (mStayOnNetworkMinimumRxRate + 1));
+        mWifiInfo.txSuccessRate = 0.0;
+        mWifiInfo.rxSuccessRate = ((double) (mStayOnNetworkMinimumRxRate + 1));
 
         testStayOrTryToSwitch(
                 mThresholdQualifiedRssi2G + 1 /* rssi before connected */,
@@ -815,9 +813,8 @@
         // Rssi after connected.
         when(mWifiInfo.getRssi()).thenReturn(mThresholdQualifiedRssi5G + 1);
         // Streaming traffic
-        when(mWifiInfo.getTxSuccessRatePps()).thenReturn(
-                (double) (mStayOnNetworkMinimumTxRate + 1));
-        when(mWifiInfo.getRxSuccessRatePps()).thenReturn(0.0);
+        mWifiInfo.txSuccessRate = ((double) (mStayOnNetworkMinimumTxRate + 1));
+        mWifiInfo.rxSuccessRate = 0.0;
 
         testStayOrTryToSwitch(
                 mThresholdQualifiedRssi5G + 1 /* rssi before connected */,
@@ -838,9 +835,8 @@
         // Rssi after connected.
         when(mWifiInfo.getRssi()).thenReturn(mThresholdQualifiedRssi5G + 1);
         // Streaming traffic
-        when(mWifiInfo.getTxSuccessRatePps()).thenReturn(0.0);
-        when(mWifiInfo.getRxSuccessRatePps()).thenReturn(
-                (double) (mStayOnNetworkMinimumRxRate + 1));
+        mWifiInfo.txSuccessRate = 0.0;
+        mWifiInfo.rxSuccessRate = ((double) (mStayOnNetworkMinimumRxRate + 1));
 
         testStayOrTryToSwitch(
                 mThresholdQualifiedRssi5G + 1 /* rssi before connected */,
diff --git a/tests/wifitests/src/com/android/server/wifi/WifiVendorHalTest.java b/tests/wifitests/src/com/android/server/wifi/WifiVendorHalTest.java
index af8bda4..812ef03 100644
--- a/tests/wifitests/src/com/android/server/wifi/WifiVendorHalTest.java
+++ b/tests/wifitests/src/com/android/server/wifi/WifiVendorHalTest.java
@@ -77,7 +77,6 @@
 import android.net.apf.ApfCapabilities;
 import android.net.wifi.RttManager;
 import android.net.wifi.ScanResult;
-import android.net.wifi.WifiLinkLayerStats;
 import android.net.wifi.WifiManager;
 import android.net.wifi.WifiScanner;
 import android.net.wifi.WifiSsid;
@@ -704,13 +703,13 @@
         randomizePacketStats(r, stats.iface.wmeVoPktStats);
         randomizeRadioStats(r, stats.radios);
 
-        stats.timeStampInMs = 42; // currently dropped in conversion
+        stats.timeStampInMs = r.nextLong() & 0xFFFFFFFFFFL;
 
-        String expected = numbersOnly(stats.toString());
+        String expected = numbersOnly(stats.toString() + " ");
 
         WifiLinkLayerStats converted = WifiVendorHal.frameworkFromHalLinkLayerStats(stats);
 
-        String actual = numbersOnly(converted.toString());
+        String actual = numbersOnly(converted.toString() + " ");
 
         // Do the required fixups to the both expected and actual
         expected = rmValue(expected, stats.radios.get(0).rxTimeInMs);
@@ -718,7 +717,6 @@
 
         actual = rmValue(actual, stats.radios.get(0).rxTimeInMs);
         actual = rmValue(actual, stats.radios.get(0).onTimeInMsForScan);
-        actual = actual + "42 ";
 
         // The remaining fields should agree
         assertEquals(expected, actual);