summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMichael Plass <mplass@google.com>2018-03-07 14:59:34 -0800
committerMichael Plass <mplass@google.com>2018-03-20 11:08:00 -0700
commitaa5fd51758c9d2d4c66a8774987cd1ebc5286c22 (patch)
tree7e0c8b99b8da4d2772e9c02f9d0439083c04b6c7
parent2d5bf2ffab544ae02b295910f55e56f163e45ec8 (diff)
[wifi_score_params] elaborations
Add ability to track changes to Settings.Global.WIFI_SCORE_PARAMS Not yet wired into framework Bug: 65216267 Test: Unit tests Change-Id: Iab69e1d92f26c9dda9574a9187aee6a9474665f0
-rw-r--r--service/java/com/android/server/wifi/ScoringParams.java181
-rw-r--r--tests/wifitests/src/com/android/server/wifi/ScoringParamsTest.java204
2 files changed, 334 insertions, 51 deletions
diff --git a/service/java/com/android/server/wifi/ScoringParams.java b/service/java/com/android/server/wifi/ScoringParams.java
index 71ec78151..1fddd7688 100644
--- a/service/java/com/android/server/wifi/ScoringParams.java
+++ b/service/java/com/android/server/wifi/ScoringParams.java
@@ -16,7 +16,13 @@
package com.android.server.wifi;
+import android.annotation.NonNull;
import android.content.Context;
+import android.database.ContentObserver;
+import android.os.Handler;
+import android.provider.Settings;
+import android.util.KeyValueListParser;
+import android.util.Log;
import com.android.internal.R;
@@ -28,36 +34,167 @@ import com.android.internal.R;
*
*/
public class ScoringParams {
+ private static final String TAG = "WifiScoringParams";
private static final int EXIT = 0;
private static final int ENTRY = 1;
private static final int SUFFICIENT = 2;
private static final int GOOD = 3;
- private final int[] mRssi2 = {-83, -80, -73, -60};
- private final int[] mRssi5 = {-80, -77, -70, -57};
+
+ /**
+ * Parameter values are stored in a separate container so that a new collection of values can
+ * be checked for consistency before activating them.
+ */
+ private class Values {
+ public static final String KEY_RSSI2 = "rssi2";
+ public static final String KEY_RSSI5 = "rssi5";
+ public static final String KEY_HORIZON = "horizon"; // number of seconds for rssi forecast
+
+ public final int[] rssi2 = {-83, -80, -73, -60};
+ public final int[] rssi5 = {-80, -77, -70, -57};
+ public int horizon = 15;
+
+ Values() {
+ }
+
+ Values(Values source) {
+ for (int i = 0; i < rssi2.length; i++) {
+ rssi2[i] = source.rssi2[i];
+ }
+ for (int i = 0; i < rssi5.length; i++) {
+ rssi5[i] = source.rssi5[i];
+ }
+ horizon = source.horizon;
+ }
+
+ public void parseString(String kvList) throws IllegalArgumentException {
+ KeyValueListParser parser = new KeyValueListParser(',');
+ parser.setString(kvList);
+ updateIntArray(rssi2, parser, KEY_RSSI2);
+ updateIntArray(rssi5, parser, KEY_RSSI5);
+ horizon = updateInt(parser, KEY_HORIZON, horizon);
+ }
+
+ private int updateInt(KeyValueListParser parser, String key, int defaultValue)
+ throws IllegalArgumentException {
+ String value = parser.getString(key, null);
+ if (value == null) return defaultValue;
+ try {
+ return Integer.parseInt(value);
+ } catch (NumberFormatException e) {
+ throw new IllegalArgumentException();
+ }
+ }
+
+ private void updateIntArray(final int[] dest, KeyValueListParser parser, String key)
+ throws IllegalArgumentException {
+ if (parser.getString(key, null) == null) return;
+ int[] ints = parser.getIntArray(key, null);
+ if (ints == null) throw new IllegalArgumentException();
+ if (ints.length != dest.length) throw new IllegalArgumentException();
+ for (int i = 0; i < dest.length; i++) {
+ dest[i] = ints[i];
+ }
+ }
+
+ @Override
+ public String toString() {
+ StringBuilder sb = new StringBuilder();
+ appendKey(sb, KEY_RSSI2);
+ appendInts(sb, rssi2);
+ appendKey(sb, KEY_RSSI5);
+ appendInts(sb, rssi5);
+ appendKey(sb, KEY_HORIZON);
+ sb.append(horizon);
+ return sb.toString();
+ }
+
+ private void appendKey(StringBuilder sb, String key) {
+ if (sb.length() != 0) sb.append(",");
+ sb.append(key).append("=");
+ }
+
+ private void appendInts(StringBuilder sb, final int[] a) {
+ final int n = a.length;
+ for (int i = 0; i < n; i++) {
+ if (i > 0) sb.append(":");
+ sb.append(a[i]);
+ }
+ }
+ }
+
+ @NonNull private Values mVal = new Values();
public ScoringParams() {
}
public ScoringParams(Context context) {
- mRssi2[EXIT] = context.getResources().getInteger(
+ loadResources(context);
+ }
+
+ public ScoringParams(Context context, FrameworkFacade facade, Handler handler) {
+ loadResources(context);
+ setupContentObserver(context, facade, handler);
+ }
+
+ private void loadResources(Context context) {
+ mVal.rssi2[EXIT] = context.getResources().getInteger(
R.integer.config_wifi_framework_wifi_score_bad_rssi_threshold_24GHz);
- mRssi2[ENTRY] = context.getResources().getInteger(
+ mVal.rssi2[ENTRY] = context.getResources().getInteger(
R.integer.config_wifi_framework_wifi_score_entry_rssi_threshold_24GHz);
- mRssi2[SUFFICIENT] = context.getResources().getInteger(
+ mVal.rssi2[SUFFICIENT] = context.getResources().getInteger(
R.integer.config_wifi_framework_wifi_score_low_rssi_threshold_24GHz);
- mRssi2[GOOD] = context.getResources().getInteger(
+ mVal.rssi2[GOOD] = context.getResources().getInteger(
R.integer.config_wifi_framework_wifi_score_good_rssi_threshold_24GHz);
- mRssi5[EXIT] = context.getResources().getInteger(
+ mVal.rssi5[EXIT] = context.getResources().getInteger(
R.integer.config_wifi_framework_wifi_score_bad_rssi_threshold_5GHz);
- mRssi5[ENTRY] = context.getResources().getInteger(
+ mVal.rssi5[ENTRY] = context.getResources().getInteger(
R.integer.config_wifi_framework_wifi_score_entry_rssi_threshold_5GHz);
- mRssi5[SUFFICIENT] = context.getResources().getInteger(
+ mVal.rssi5[SUFFICIENT] = context.getResources().getInteger(
R.integer.config_wifi_framework_wifi_score_low_rssi_threshold_5GHz);
- mRssi5[GOOD] = context.getResources().getInteger(
+ mVal.rssi5[GOOD] = context.getResources().getInteger(
R.integer.config_wifi_framework_wifi_score_good_rssi_threshold_5GHz);
}
- private static final int MINIMUM_5GHZ_BAND_FREQUENCY_IN_MEGAHERTZ = 5000;
+ private void setupContentObserver(Context context, FrameworkFacade facade, Handler handler) {
+ final ScoringParams self = this;
+ String defaults = self.toString();
+ ContentObserver observer = new ContentObserver(handler) {
+ @Override
+ public void onChange(boolean selfChange) {
+ String params = facade.getStringSetting(
+ context, Settings.Global.WIFI_SCORE_PARAMS);
+ if (params != null) {
+ self.update(defaults);
+ if (!self.update(params)) {
+ Log.e(TAG, "Error in " + Settings.Global.WIFI_SCORE_PARAMS + ": " + params);
+ }
+ }
+ Log.i(TAG, self.toString());
+ }
+ };
+ facade.registerContentObserver(context,
+ Settings.Global.getUriFor(Settings.Global.WIFI_SCORE_PARAMS),
+ true,
+ observer);
+ observer.onChange(false);
+ }
+
+ /**
+ * Updates the parameters from the given parameter string.
+ * If any errors are detected, no change is made.
+ * @param kvList is a comma-separated key=value list.
+ * @return true for success
+ */
+ public boolean update(String kvList) {
+ Values v = new Values(mVal);
+ try {
+ v.parseString(kvList);
+ mVal = v;
+ return true;
+ } catch (IllegalArgumentException e) {
+ return false;
+ }
+ }
/** Constant to denote someplace in the 2.4 GHz band */
public static final int BAND2 = 2400;
@@ -95,11 +232,25 @@ public class ScoringParams {
return getRssiArray(frequencyMegaHertz)[GOOD];
}
- private int[] getRssiArray(int frequencyMegaHertz) {
- if (frequencyMegaHertz < MINIMUM_5GHZ_BAND_FREQUENCY_IN_MEGAHERTZ) {
- return mRssi2;
+ /**
+ * Returns the number of seconds to use for rssi forecast.
+ */
+ public int getHorizonSeconds() {
+ return mVal.horizon;
+ }
+
+ private static final int MINIMUM_5GHZ_BAND_FREQUENCY_IN_MEGAHERTZ = 5000;
+
+ private int[] getRssiArray(int frequency) {
+ if (frequency < MINIMUM_5GHZ_BAND_FREQUENCY_IN_MEGAHERTZ) {
+ return mVal.rssi2;
} else {
- return mRssi5;
+ return mVal.rssi5;
}
}
+
+ @Override
+ public String toString() {
+ return mVal.toString();
+ }
}
diff --git a/tests/wifitests/src/com/android/server/wifi/ScoringParamsTest.java b/tests/wifitests/src/com/android/server/wifi/ScoringParamsTest.java
index 5047701cf..6b3b720e9 100644
--- a/tests/wifitests/src/com/android/server/wifi/ScoringParamsTest.java
+++ b/tests/wifitests/src/com/android/server/wifi/ScoringParamsTest.java
@@ -17,18 +17,27 @@
package com.android.server.wifi;
import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyBoolean;
+import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import android.content.Context;
import android.content.res.Resources;
+import android.database.ContentObserver;
+import android.os.Handler;
+import android.provider.Settings;
import android.support.test.filters.SmallTest;
import com.android.internal.R;
import org.junit.Before;
import org.junit.Test;
+import org.mockito.ArgumentCaptor;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.mockito.Spy;
@@ -41,6 +50,111 @@ public class ScoringParamsTest {
ScoringParams mScoringParams;
+ /**
+ * Sets up for unit test
+ */
+ @Before
+ public void setUp() throws Exception {
+ MockitoAnnotations.initMocks(this);
+ setUpResources(mResources);
+ when(mContext.getResources()).thenReturn(mResources);
+ mScoringParams = new ScoringParams();
+ }
+
+ /**
+ * Check that thresholds are properly ordered, and in range.
+ */
+ private void checkThresholds(int frequency) {
+ assertTrue(-127 <= mScoringParams.getExitRssi(frequency));
+ assertTrue(mScoringParams.getExitRssi(frequency)
+ <= mScoringParams.getEntryRssi(frequency));
+ assertTrue(mScoringParams.getEntryRssi(frequency)
+ <= mScoringParams.getSufficientRssi(frequency));
+ assertTrue(mScoringParams.getSufficientRssi(frequency)
+ <= mScoringParams.getGoodRssi(frequency));
+ assertTrue(mScoringParams.getGoodRssi(frequency) < 0);
+ }
+
+ /**
+ * Test basic constructor
+ */
+ @Test
+ public void testBasicConstructor() throws Exception {
+ mScoringParams = new ScoringParams();
+ checkThresholds(2412);
+ checkThresholds(5020);
+ assertEquals(15, mScoringParams.getHorizonSeconds());
+ }
+
+ /**
+ * Test toString
+ */
+ @Test
+ public void testToString() throws Exception {
+ mScoringParams = new ScoringParams();
+ String expect = "rssi2=-83:-80:-73:-60,rssi5=-80:-77:-70:-57,horizon=15";
+ String actual = mScoringParams.toString();
+ assertEquals(expect, actual);
+ }
+
+ /**
+ * Test complete update
+ */
+ @Test
+ public void testUpdateEverything() throws Exception {
+ mScoringParams = new ScoringParams();
+ String params = "rssi2=-86:-84:-77:-10,rssi5=-88:-77:-66:-55,horizon=3";
+ assertTrue(mScoringParams.update(params));
+ assertEquals(params, mScoringParams.toString());
+ }
+
+ /**
+ * Test partial update
+ */
+ @Test
+ public void testPartialUpdate() throws Exception {
+ mScoringParams = new ScoringParams();
+ String before = mScoringParams.toString();
+ String partial = "rssi5=-88:-77:-66:-55";
+ assertFalse(before.contains(partial));
+ assertTrue(mScoringParams.update(partial));
+ String after = mScoringParams.toString();
+ assertTrue(after + " should contain " + partial, after.contains(partial));
+ }
+
+ /**
+ * Test some failed updates
+ */
+ @Test
+ public void testUpdateFail() throws Exception {
+ mScoringParams = new ScoringParams();
+ String before = mScoringParams.toString();
+ assertFalse(mScoringParams.update("word"));
+ assertFalse(mScoringParams.update("42"));
+ assertFalse(mScoringParams.update(" "));
+ assertFalse(mScoringParams.update("horizon=flat"));
+ assertFalse(mScoringParams.update(",,,,,,,,,,,,,,,,,,"));
+ assertFalse(mScoringParams.update("rssi2=-86"));
+ assertFalse(mScoringParams.update("rssi2=-99:-88:-77:-66:-55"));
+ assertFalse(mScoringParams.update("rssi5=one:two:three:four"));
+ assertEquals(before, mScoringParams.toString());
+ }
+
+ /**
+ * Test that empty updates are OK
+ */
+ @Test
+ public void testEmptyUpdate() throws Exception {
+ mScoringParams = new ScoringParams();
+ String before = mScoringParams.toString();
+ assertTrue(mScoringParams.update(""));
+ assertTrue(mScoringParams.update(null));
+ assertEquals(before, mScoringParams.toString());
+ }
+
+ /**
+ * Tests for obtaining values from device configuration (config.xml)
+ */
int mBad2GHz, mEntry2GHz, mSufficient2GHz, mGood2GHz;
int mBad5GHz, mEntry5GHz, mSufficient5GHz, mGood5GHz;
@@ -77,45 +191,10 @@ public class ScoringParamsTest {
}
/**
- * Sets up for unit test
- */
- @Before
- public void setUp() throws Exception {
- MockitoAnnotations.initMocks(this);
- setUpResources(mResources);
- when(mContext.getResources()).thenReturn(mResources);
- mScoringParams = new ScoringParams();
- }
-
- /**
- * Check that thresholds are properly ordered, and in range.
- */
- private void checkThresholds(int frequency) {
- assertTrue(-127 <= mScoringParams.getExitRssi(frequency));
- assertTrue(mScoringParams.getExitRssi(frequency)
- <= mScoringParams.getEntryRssi(frequency));
- assertTrue(mScoringParams.getEntryRssi(frequency)
- <= mScoringParams.getSufficientRssi(frequency));
- assertTrue(mScoringParams.getSufficientRssi(frequency)
- <= mScoringParams.getGoodRssi(frequency));
- assertTrue(mScoringParams.getGoodRssi(frequency) < 0);
- }
-
- /**
- * Test basic constuctor
- */
- @Test
- public void testBasicConstructor() {
- mScoringParams = new ScoringParams();
- checkThresholds(2412);
- checkThresholds(5020);
- }
-
- /**
* Check that we get the config.xml values, if that's what we want
*/
@Test
- public void testContextConstructor() {
+ public void testContextConstructor() throws Exception {
mScoringParams = new ScoringParams(mContext);
assertEquals(mBad2GHz, mScoringParams.getExitRssi(2412));
@@ -130,4 +209,57 @@ public class ScoringParamsTest {
assertEquals(mGood5GHz, mScoringParams.getGoodRssi(5678));
assertEquals(mGood5GHz, mScoringParams.getGoodRssi(ScoringParams.BAND5));
}
+
+ /**
+ * Additional mocks for handling Settings
+ */
+ @Mock FrameworkFacade mFrameworkFacade;
+ @Mock Handler mHandler;
+
+ /**
+ * Test getting updates from Settings
+ *
+ * Exercises the ContentObserver notification path
+ */
+ @Test
+ public void testFullConstructorWithUpdatesFromSettings() throws Exception {
+ ArgumentCaptor<ContentObserver> captor = ArgumentCaptor.forClass(ContentObserver.class);
+ when(mFrameworkFacade.getStringSetting(mContext, Settings.Global.WIFI_SCORE_PARAMS))
+ .thenReturn(null);
+ mScoringParams = new ScoringParams(mContext, mFrameworkFacade, mHandler);
+ verify(mFrameworkFacade)
+ .registerContentObserver(eq(mContext), any(), anyBoolean(), captor.capture());
+
+ String before = mScoringParams.toString();
+ String changed = before.replace('8', '9');
+ assertFalse(changed.equals(before));
+
+ when(mFrameworkFacade.getStringSetting(mContext, Settings.Global.WIFI_SCORE_PARAMS))
+ .thenReturn(changed);
+ captor.getValue().onChange(/*selfChange*/ false);
+ assertEquals(changed, mScoringParams.toString());
+
+ when(mFrameworkFacade.getStringSetting(mContext, Settings.Global.WIFI_SCORE_PARAMS))
+ .thenReturn("");
+ captor.getValue().onChange(/*selfChange*/ false);
+ assertEquals(before, mScoringParams.toString());
+ }
+
+ @Test
+ public void testBadSettings() throws Exception {
+ ArgumentCaptor<ContentObserver> captor = ArgumentCaptor.forClass(ContentObserver.class);
+ when(mFrameworkFacade.getStringSetting(mContext, Settings.Global.WIFI_SCORE_PARAMS))
+ .thenReturn(null);
+ mScoringParams = new ScoringParams(mContext, mFrameworkFacade, mHandler);
+ verify(mFrameworkFacade)
+ .registerContentObserver(eq(mContext), any(), anyBoolean(), captor.capture());
+
+ String before = mScoringParams.toString();
+ String garbage = "what??";
+
+ when(mFrameworkFacade.getStringSetting(mContext, Settings.Global.WIFI_SCORE_PARAMS))
+ .thenReturn(garbage);
+ captor.getValue().onChange(/*selfChange*/ false);
+ assertEquals(before, mScoringParams.toString());
+ }
}