summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorQuang Luong <qal@google.com>2019-06-20 15:55:08 -0700
committerQuang Luong <qal@google.com>2019-07-02 10:44:01 -0700
commit9e4fd8a79d30d06174c332ab412f121bebf05caf (patch)
tree7a4303777c86f45e8406a51fcf08728c41710f41
parent18037f7e54c1ec2805b377eff666bec3d314a78b (diff)
Added ScanResultMerger utility class to keep running list of scans
ScanResultMerger will be used by WifiTracker2 to help keep track of new scans and merge them by BSSID. Bug: 70983952 Test: atest ScanResultUpdaterTest Change-Id: Ib587b83d3e89fcec255216ea6f6bb52a57376279
-rw-r--r--libs/WifiTrackerLib/src/com/android/wifitrackerlib/ScanResultUpdater.java100
-rw-r--r--libs/WifiTrackerLib/tests/Android.bp28
-rw-r--r--libs/WifiTrackerLib/tests/AndroidManifest.xml36
-rw-r--r--libs/WifiTrackerLib/tests/src/com/android/wifitrackerlib/ScanResultUpdaterTest.java159
4 files changed, 323 insertions, 0 deletions
diff --git a/libs/WifiTrackerLib/src/com/android/wifitrackerlib/ScanResultUpdater.java b/libs/WifiTrackerLib/src/com/android/wifitrackerlib/ScanResultUpdater.java
new file mode 100644
index 000000000..6df4f266c
--- /dev/null
+++ b/libs/WifiTrackerLib/src/com/android/wifitrackerlib/ScanResultUpdater.java
@@ -0,0 +1,100 @@
+/*
+ * 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 com.android.wifitrackerlib;
+
+import android.net.wifi.ScanResult;
+import android.os.SystemClock;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+
+/**
+ * Utility class to keep a running list of scan results merged by BSSID.
+ *
+ * Thread-safe.
+ */
+public class ScanResultUpdater {
+ private HashMap<String, ScanResult> mScanResultsByBssid = new HashMap<>();
+ private long mMaxScanAgeMillis;
+ private Object mLock = new Object();
+
+ /**
+ * Creates a ScanResultUpdater with no max scan age.
+ */
+ public ScanResultUpdater() {
+ this(Long.MAX_VALUE);
+ }
+
+ /**
+ * Creates a ScanResultUpdater with a max scan age in milliseconds. Scans older than this limit
+ * will be pruned upon update/retrieval to keep the size of the scan list down.
+ */
+ public ScanResultUpdater(long maxScanAgeMillis) {
+ mMaxScanAgeMillis = maxScanAgeMillis;
+ }
+
+ /**
+ * Updates scan result list and replaces older scans of the same BSSID.
+ */
+ public void update(List<ScanResult> newResults) {
+ synchronized (mLock) {
+ evictOldScans();
+
+ for (ScanResult result : newResults) {
+ ScanResult prevResult = mScanResultsByBssid.get(result.BSSID);
+ if (prevResult == null || (prevResult.timestamp < result.timestamp)) {
+ mScanResultsByBssid.put(result.BSSID, result);
+ }
+ }
+ }
+ }
+
+ /**
+ * Returns all seen scan results merged by BSSID.
+ */
+ public List<ScanResult> getScanResults() {
+ return getScanResults(mMaxScanAgeMillis);
+ }
+
+ /**
+ * Returns all seen scan results merged by BSSID and newer than maxScanAgeMillis.
+ * maxScanAgeMillis must be less than or equal to the mMaxScanAgeMillis field if it was set.
+ */
+ public List<ScanResult> getScanResults(long maxScanAgeMillis) throws IllegalArgumentException {
+ if (maxScanAgeMillis > mMaxScanAgeMillis) {
+ throw new IllegalArgumentException(
+ "maxScanAgeMillis argument cannot be greater than mMaxScanAgeMillis!");
+ }
+ synchronized (mLock) {
+ List<ScanResult> ageFilteredResults = new ArrayList<>();
+ for (ScanResult result : mScanResultsByBssid.values()) {
+ if (SystemClock.elapsedRealtime() - result.timestamp <= maxScanAgeMillis) {
+ ageFilteredResults.add(result);
+ }
+ }
+ return ageFilteredResults;
+ }
+ }
+
+ private void evictOldScans() {
+ synchronized (mLock) {
+ mScanResultsByBssid.entrySet().removeIf((entry) ->
+ SystemClock.elapsedRealtime() - entry.getValue().timestamp > mMaxScanAgeMillis);
+ }
+ }
+}
diff --git a/libs/WifiTrackerLib/tests/Android.bp b/libs/WifiTrackerLib/tests/Android.bp
new file mode 100644
index 000000000..e8f854893
--- /dev/null
+++ b/libs/WifiTrackerLib/tests/Android.bp
@@ -0,0 +1,28 @@
+// 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.
+
+android_test {
+ name: "WifiTrackerLibTests",
+ srcs: ["src/**/*.java"],
+
+ static_libs: [
+ "android-support-test",
+ "androidx.test.rules",
+ "WifiTrackerLib",
+ ],
+
+ platform_apis: true,
+
+ test_suites: ["device-tests"],
+}
diff --git a/libs/WifiTrackerLib/tests/AndroidManifest.xml b/libs/WifiTrackerLib/tests/AndroidManifest.xml
new file mode 100644
index 000000000..2642960e7
--- /dev/null
+++ b/libs/WifiTrackerLib/tests/AndroidManifest.xml
@@ -0,0 +1,36 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.android.wifitrackerlib.test" >
+
+ <application>
+ <uses-library android:name="android.test.runner" />
+ <activity android:label="WifiTrackerTestsDummyLabel"
+ android:name="WifiTrackerTestsDummyName">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN" />
+ <category android:name="android.intent.category.LAUNCHER"/>
+ </intent-filter>
+ </activity>
+ </application>
+
+ <instrumentation android:name="androidx.test.runner.AndroidJUnitRunner"
+ android:targetPackage="com.android.wifitrackerlib.test"
+ android:label="WifiTrackerLib Tests">
+ </instrumentation>
+
+</manifest> \ No newline at end of file
diff --git a/libs/WifiTrackerLib/tests/src/com/android/wifitrackerlib/ScanResultUpdaterTest.java b/libs/WifiTrackerLib/tests/src/com/android/wifitrackerlib/ScanResultUpdaterTest.java
new file mode 100644
index 000000000..39d6bf3f8
--- /dev/null
+++ b/libs/WifiTrackerLib/tests/src/com/android/wifitrackerlib/ScanResultUpdaterTest.java
@@ -0,0 +1,159 @@
+/*
+ * 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 com.android.wifitrackerlib;
+
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.Matchers.contains;
+import static org.hamcrest.Matchers.equalTo;
+import static org.junit.Assert.fail;
+
+import android.net.wifi.ScanResult;
+import android.os.SystemClock;
+
+import org.junit.Test;
+
+import java.util.Arrays;
+import java.util.List;
+
+public class ScanResultUpdaterTest {
+ private static final String BSSID_1 = "11:11:11:11:11:11";
+ private static final String BSSID_2 = "22:22:22:22:22:22";
+ private static final String BSSID_3 = "33:33:33:33:33:33";
+
+ private static ScanResult buildScanResult(String bssid, long timestampMs) {
+ return new ScanResult(
+ null,
+ bssid,
+ 0, // hessid
+ 0, //anqpDomainId
+ null, // osuProviders
+ "", // capabilities
+ 0,
+ 0, // frequency
+ timestampMs /* microsecond timestamp */);
+ }
+
+ /**
+ * Verify that scan results of the same BSSID are merged to latest one.
+ */
+ @Test
+ public void testGetScanResults_mergeSameBssid() {
+ ScanResult oldResult = buildScanResult(
+ BSSID_1, 10);
+ ScanResult newResult = buildScanResult(
+ BSSID_1, 20);
+
+ // Add initial scan result. List should have 1 scan.
+ ScanResultUpdater sru = new ScanResultUpdater();
+ sru.update(Arrays.asList(oldResult));
+ assertThat(sru.getScanResults().size(), equalTo(1));
+ assertThat(sru.getScanResults().get(0), equalTo(oldResult));
+
+ // Add new scan result. Old scan result should be replaced.
+ sru.update(Arrays.asList(newResult));
+ assertThat(sru.getScanResults().size(), equalTo(1));
+ assertThat(sru.getScanResults().get(0), equalTo(newResult));
+
+ // Add old scan result back. New scan result should still remain.
+ sru.update(Arrays.asList(oldResult));
+ assertThat(sru.getScanResults().size(), equalTo(1));
+ assertThat(sru.getScanResults().get(0), equalTo(newResult));
+ }
+
+ /**
+ * Verify that scan results are filtered out by age.
+ */
+ @Test
+ public void testGetScanResults_filtersOldScans() {
+ long now = SystemClock.elapsedRealtime();
+ long maxScanAge = 15_000;
+
+ ScanResult oldResult = buildScanResult(
+ BSSID_1, now - (maxScanAge + 1));
+ ScanResult newResult = buildScanResult(
+ BSSID_2, now);
+
+ // Add a new scan result and an out-of-date scan result.
+ ScanResultUpdater sru = new ScanResultUpdater();
+ sru.update(Arrays.asList(newResult, oldResult));
+
+ // New scan result should remain and out-of-date scan result should not be returned.
+ assertThat(sru.getScanResults(maxScanAge).size(), equalTo(1));
+ assertThat(sru.getScanResults(maxScanAge).get(0), equalTo(newResult));
+ }
+
+ /**
+ * Verify that an exception is thrown if the getScanResults max scan age is larger than the
+ * constructor's max scan age.
+ */
+ @Test
+ public void testGetScanResults_invalidMaxScanAgeMillis_throwsException() {
+ ScanResultUpdater sru = new ScanResultUpdater(15_000);
+ try {
+ sru.getScanResults(20_000);
+ fail("Should have thrown exception for maxScanAgeMillis too large.");
+ } catch (IllegalArgumentException ok) {
+ // Expected
+ }
+ }
+
+ /**
+ * Verify that the constructor max scan age is obeyed when getting scan results.
+ */
+ @Test
+ public void testConstructor_maxScanAge_filtersOldScans() {
+ ScanResultUpdater sru = new ScanResultUpdater(15_000);
+ long now = SystemClock.elapsedRealtime();
+
+ ScanResult scan1 = buildScanResult(BSSID_1, now - 10_000);
+ ScanResult scan2 = buildScanResult(BSSID_2, now - 12_000);
+ ScanResult scan3 = buildScanResult(BSSID_3, now - 20_000);
+
+ sru.update(Arrays.asList(scan1, scan2, scan3));
+
+ List<ScanResult> scanResults = sru.getScanResults();
+
+ assertThat(scanResults.size(), equalTo(2));
+ assertThat(scanResults, contains(scan1, scan2));
+ }
+
+ /**
+ * Verify that getScanResults returns results aged by the passed in max scan age even if there
+ * is a max scan age set by the constructor.
+ */
+ @Test
+ public void testGetScanResults_overridesConstructorMaxScanAge() {
+ ScanResultUpdater sru = new ScanResultUpdater(15_000);
+ long now = SystemClock.elapsedRealtime();
+
+ ScanResult scan1 = buildScanResult(BSSID_1, now - 10_000);
+ ScanResult scan2 = buildScanResult(BSSID_2, now - 12_000);
+ ScanResult scan3 = buildScanResult(BSSID_3, now - 20_000);
+
+ sru.update(Arrays.asList(scan1, scan2, scan3));
+
+ // Aged getScanResults should override the constructor max scan age.
+ List<ScanResult> scanResults = sru.getScanResults(11_000);
+ assertThat(scanResults.size(), equalTo(1));
+ assertThat(scanResults, contains(scan1));
+
+ // Non-aged getScanResults should revert to the constructor max scan age.
+ scanResults = sru.getScanResults();
+ assertThat(scanResults.size(), equalTo(2));
+ assertThat(scanResults, contains(scan1, scan2));
+ }
+}