summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--service/java/com/android/server/wifi/util/MissingCounterTimerLockList.java153
-rw-r--r--tests/wifitests/src/com/android/server/wifi/util/MissingCounterTimerLockListTest.java86
2 files changed, 239 insertions, 0 deletions
diff --git a/service/java/com/android/server/wifi/util/MissingCounterTimerLockList.java b/service/java/com/android/server/wifi/util/MissingCounterTimerLockList.java
new file mode 100644
index 000000000..efcbc596f
--- /dev/null
+++ b/service/java/com/android/server/wifi/util/MissingCounterTimerLockList.java
@@ -0,0 +1,153 @@
+/*
+ * Copyright (C) 2020 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.util;
+
+import android.annotation.NonNull;
+
+import com.android.server.wifi.Clock;
+
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * Utility class for Lock list which control by a missing counter which will trigger a timer when
+ * reach the threshold. When timer expires, object will be unlocked. Before unlocked, if object
+ * present again, will reset both counter and timer.
+ * @param <E>
+ */
+public class MissingCounterTimerLockList<E> {
+ private final int mConsecutiveMissingCountToTriggerTimer;
+ private final Clock mClock;
+ private final Map<E, LockListEntry> mEntries;
+
+ /**
+ * Create a new MissingCounterTimerLockList.
+ * @param consecutiveCount missing count threshold.
+ * @param clock system clock.
+ */
+ public MissingCounterTimerLockList(int consecutiveCount, Clock clock) {
+ mConsecutiveMissingCountToTriggerTimer = consecutiveCount;
+ mClock = clock;
+ mEntries = new HashMap<>();
+ }
+
+ /**
+ * Update a set of object to check if object present or not.
+ * @param entrySet set of objects.
+ */
+ public void update(@NonNull Set<E> entrySet) {
+ if (entrySet == null) {
+ return;
+ }
+ for (Map.Entry<E, LockListEntry> mapEntry : mEntries.entrySet()) {
+ if (mapEntry.getValue().isExpired()) {
+ continue;
+ }
+ if (entrySet.contains(mapEntry.getKey())) {
+ mapEntry.getValue().onPresent();
+ } else {
+ mapEntry.getValue().onAbsent();
+ }
+ }
+ }
+
+ /**
+ * Add a object to lock with timer duration
+ * @param entry object to lock.
+ * @param duration duration of the timer.
+ */
+ public void add(@NonNull E entry, long duration) {
+ if (entry == null) {
+ return;
+ }
+ mEntries.put(entry, new LockListEntry(duration));
+ }
+
+ /**
+ * Remove an object from the lock list.
+ * @param entry object to remove.
+ * @return true if lock list contains this element, otherwise false.
+ */
+ public boolean remove(@NonNull E entry) {
+ return mEntries.remove(entry) != null;
+ }
+
+ /**
+ * Check if an object is in lock list and still locked.
+ * @param entry object to check.
+ * @return true if the object is in the list and locking, otherwise false.
+ */
+ public boolean isLocked(@NonNull E entry) {
+ if (entry == null) {
+ return false;
+ }
+ LockListEntry blockTimer = mEntries.get(entry);
+ return blockTimer != null && !blockTimer.isExpired();
+ }
+
+ /**
+ * Return the size of the lock list
+ */
+ public int size() {
+ return mEntries.size();
+ }
+
+ /**
+ * Clear the whole lock list.
+ */
+ public void clear() {
+ mEntries.clear();
+ }
+
+ class LockListEntry {
+ private final long mExpiryMs;
+ private long mStartTimeStamp;
+ private int mCount;
+
+ LockListEntry(long expiryMs) {
+ mCount = mConsecutiveMissingCountToTriggerTimer;
+ mExpiryMs = expiryMs;
+ mStartTimeStamp = mClock.getWallClockMillis();
+ }
+
+ void onPresent() {
+ if (isExpired()) {
+ return;
+ }
+ mCount = mConsecutiveMissingCountToTriggerTimer;
+ mStartTimeStamp = mClock.getWallClockMillis();
+ }
+
+ void onAbsent() {
+ if (mCount == 0) {
+ // Timer already triggered
+ return;
+ }
+ mCount--;
+ if (mCount > 0) {
+ // Don't need to trigger timer
+ return;
+ }
+ mStartTimeStamp = mClock.getWallClockMillis();
+ }
+
+ boolean isExpired() {
+ return mCount == 0 && mStartTimeStamp + mExpiryMs < mClock.getWallClockMillis();
+ }
+ }
+}
diff --git a/tests/wifitests/src/com/android/server/wifi/util/MissingCounterTimerLockListTest.java b/tests/wifitests/src/com/android/server/wifi/util/MissingCounterTimerLockListTest.java
new file mode 100644
index 000000000..c145f7fb7
--- /dev/null
+++ b/tests/wifitests/src/com/android/server/wifi/util/MissingCounterTimerLockListTest.java
@@ -0,0 +1,86 @@
+/*
+ * Copyright (C) 2020 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.util;
+
+import static org.junit.Assert.*;
+import static org.mockito.Mockito.when;
+
+import androidx.test.filters.SmallTest;
+
+import com.android.server.wifi.Clock;
+import com.android.server.wifi.WifiBaseTest;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+import java.util.HashSet;
+import java.util.Set;
+
+@SmallTest
+public class MissingCounterTimerLockListTest extends WifiBaseTest {
+ private static final int TEST_MISSING_COUNT = 2;
+ private static final long BLOCKING_DURATION = 1000;
+ private static final String TEST_SSID = "testSSID";
+ private static final String TEST_FQDN = "testFQDN";
+
+ private MissingCounterTimerLockList<String> mMissingCounterTimerLockList;
+
+ @Mock Clock mClock;
+
+ @Before
+ public void setUp() {
+ MockitoAnnotations.initMocks(this);
+ mMissingCounterTimerLockList =
+ new MissingCounterTimerLockList<>(TEST_MISSING_COUNT, mClock);
+ }
+
+ @Test
+ public void testAddRemove() {
+ mMissingCounterTimerLockList.add(TEST_SSID, BLOCKING_DURATION);
+ mMissingCounterTimerLockList.add(TEST_FQDN, BLOCKING_DURATION);
+ mMissingCounterTimerLockList.add(null, BLOCKING_DURATION);
+ assertEquals(2, mMissingCounterTimerLockList.size());
+ assertTrue(mMissingCounterTimerLockList.isLocked(TEST_SSID));
+ assertTrue(mMissingCounterTimerLockList.isLocked(TEST_FQDN));
+ assertTrue(mMissingCounterTimerLockList.remove(TEST_SSID));
+ assertEquals(1, mMissingCounterTimerLockList.size());
+ assertFalse(mMissingCounterTimerLockList.isLocked(TEST_SSID));
+ assertTrue(mMissingCounterTimerLockList.isLocked(TEST_FQDN));
+ mMissingCounterTimerLockList.clear();
+ assertEquals(0, mMissingCounterTimerLockList.size());
+ }
+
+ @Test
+ public void testUpdateAndTimer() {
+ mMissingCounterTimerLockList.add(TEST_SSID, BLOCKING_DURATION);
+ mMissingCounterTimerLockList.add(TEST_FQDN, BLOCKING_DURATION);
+ assertEquals(2, mMissingCounterTimerLockList.size());
+ when(mClock.getWallClockMillis()).thenReturn((long) 0);
+ Set<String> updateSet = new HashSet<>();
+ updateSet.add(TEST_FQDN);
+ mMissingCounterTimerLockList.update(updateSet);
+ assertEquals(2, mMissingCounterTimerLockList.size());
+ mMissingCounterTimerLockList.update(updateSet);
+ assertEquals(2, mMissingCounterTimerLockList.size());
+ when(mClock.getWallClockMillis()).thenReturn(BLOCKING_DURATION + 50);
+ assertEquals(2, mMissingCounterTimerLockList.size());
+ assertFalse(mMissingCounterTimerLockList.isLocked(TEST_SSID));
+ assertTrue(mMissingCounterTimerLockList.isLocked(TEST_FQDN));
+ }
+}