diff --git a/packages/SettingsLib/src/com/android/settingslib/wifi/WifiStateWorker.java b/packages/SettingsLib/src/com/android/settingslib/wifi/WifiStateWorker.java new file mode 100644 index 0000000000000..a0c2698da4b54 --- /dev/null +++ b/packages/SettingsLib/src/com/android/settingslib/wifi/WifiStateWorker.java @@ -0,0 +1,165 @@ +/* + * Copyright (C) 2022 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.settingslib.wifi; + +import static android.net.wifi.WifiManager.EXTRA_WIFI_STATE; +import static android.net.wifi.WifiManager.WIFI_STATE_CHANGED_ACTION; +import static android.net.wifi.WifiManager.WIFI_STATE_DISABLED; +import static android.net.wifi.WifiManager.WIFI_STATE_ENABLED; + +import android.annotation.AnyThread; +import android.annotation.NonNull; +import android.annotation.TestApi; +import android.content.BroadcastReceiver; +import android.content.Context; +import android.content.Intent; +import android.content.IntentFilter; +import android.net.wifi.WifiManager; +import android.os.HandlerThread; +import android.os.Process; +import android.util.Log; + +import androidx.annotation.GuardedBy; +import androidx.annotation.VisibleForTesting; +import androidx.annotation.WorkerThread; + +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; + +/** + * This is a singleton class for Wi-Fi state worker. + */ +public class WifiStateWorker { + + private static final String TAG = "WifiStateWorker"; + private static final Object sLock = new Object(); + + /** + * A singleton {@link WifiStateWorker} object is used to share with all sub-settings. + */ + @GuardedBy("sLock") + private static WifiStateWorker sInstance; + @TestApi + @GuardedBy("sLock") + private static Map sTestInstances; + + @VisibleForTesting + static WifiManager sWifiManager; + private static int sWifiState; + private static HandlerThread sWorkerThread; + + /** + * Static method to create a singleton class for WifiStateWorker. + * + * @param context The Context this is associated with. + * @return an instance of {@link WifiStateWorker} object. + */ + @NonNull + @AnyThread + public static WifiStateWorker getInstance(@NonNull Context context) { + synchronized (sLock) { + if (sTestInstances != null && sTestInstances.containsKey(context)) { + WifiStateWorker testInstance = sTestInstances.get(context); + Log.w(TAG, "The context owner try to use a test instance:" + testInstance); + return testInstance; + } + + if (sInstance != null) return sInstance; + + sInstance = new WifiStateWorker(); + sWorkerThread = new HandlerThread( + TAG + ":{" + context.getApplicationInfo().className + "}", + Process.THREAD_PRIORITY_DISPLAY); + sWorkerThread.start(); + sWorkerThread.getThreadHandler().post(() -> init(context)); + return sInstance; + } + } + + /** + * A convenience method to set pre-prepared instance or mock(WifiStateWorker.class) for testing. + * + * @param context The Context this is associated with. + * @param instance of {@link WifiStateWorker} object. + * @hide + */ + @TestApi + @VisibleForTesting + public static void setTestInstance(@NonNull Context context, WifiStateWorker instance) { + synchronized (sLock) { + if (sTestInstances == null) sTestInstances = new ConcurrentHashMap<>(); + + Log.w(TAG, "Try to set a test instance by context:" + context); + sTestInstances.put(context, instance); + } + } + + @WorkerThread + private static void init(@NonNull Context context) { + final Context appContext = context.getApplicationContext(); + final IntentReceiver receiver = new IntentReceiver(); + appContext.registerReceiver(receiver, new IntentFilter(WIFI_STATE_CHANGED_ACTION), null, + sWorkerThread.getThreadHandler()); + sWifiManager = appContext.getSystemService(WifiManager.class); + refresh(); + } + + /** + * Refresh Wi-Fi state with WifiManager#getWifiState() + */ + @AnyThread + public static void refresh() { + if (sWifiManager == null) return; + Log.d(TAG, "Start calling WifiManager#getWifiState."); + sWifiState = sWifiManager.getWifiState(); + Log.d(TAG, "WifiManager#getWifiState return state:" + sWifiState); + } + + /** + * Gets the Wi-Fi enabled state. + * + * @return One of {@link WifiManager#WIFI_STATE_DISABLED}, + * {@link WifiManager#WIFI_STATE_DISABLING}, {@link WifiManager#WIFI_STATE_ENABLED}, + * {@link WifiManager#WIFI_STATE_ENABLING}, {@link WifiManager#WIFI_STATE_UNKNOWN} + * @see #isWifiEnabled() + */ + @AnyThread + public int getWifiState() { + return sWifiState; + } + + /** + * Return whether Wi-Fi is enabled or disabled. + * + * @return {@code true} if Wi-Fi is enabled + * @see #getWifiState() + */ + @AnyThread + public boolean isWifiEnabled() { + return sWifiState == WIFI_STATE_ENABLED; + } + + @WorkerThread + private static class IntentReceiver extends BroadcastReceiver { + @Override + public void onReceive(Context context, Intent intent) { + if (WIFI_STATE_CHANGED_ACTION.equals(intent.getAction())) { + sWifiState = intent.getIntExtra(EXTRA_WIFI_STATE, WIFI_STATE_DISABLED); + } + } + } +} diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/wifi/WifiStateWorkerTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/wifi/WifiStateWorkerTest.java new file mode 100644 index 0000000000000..2589a90e33c27 --- /dev/null +++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/wifi/WifiStateWorkerTest.java @@ -0,0 +1,136 @@ +/* + * Copyright (C) 2022 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.settingslib.wifi; + +import static android.net.wifi.WifiManager.WIFI_STATE_DISABLED; +import static android.net.wifi.WifiManager.WIFI_STATE_DISABLING; +import static android.net.wifi.WifiManager.WIFI_STATE_ENABLED; +import static android.net.wifi.WifiManager.WIFI_STATE_ENABLING; + +import static com.google.common.truth.Truth.assertThat; + +import static org.mockito.Mockito.when; + +import android.content.Context; +import android.net.wifi.WifiManager; +import android.os.UserHandle; + +import androidx.test.core.app.ApplicationProvider; + +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.Spy; +import org.mockito.junit.MockitoJUnit; +import org.mockito.junit.MockitoRule; +import org.robolectric.RobolectricTestRunner; + +@RunWith(RobolectricTestRunner.class) +public class WifiStateWorkerTest { + + @Rule + public final MockitoRule mMockitoRule = MockitoJUnit.rule(); + @Spy + Context mContext = ApplicationProvider.getApplicationContext(); + @Mock + WifiManager mWifiManager; + + WifiStateWorker mWifiStateWorker; + + @Before + public void setUp() { + when(mWifiManager.getWifiState()).thenReturn(WIFI_STATE_ENABLED); + WifiStateWorker.sWifiManager = mWifiManager; + + mWifiStateWorker = WifiStateWorker.getInstance(mContext); + } + + @Test + public void getInstance_diffContext_getSameInstance() { + Context context = mContext.createContextAsUser(UserHandle.ALL, 0 /* flags */); + WifiStateWorker instance = WifiStateWorker.getInstance(context); + + assertThat(mContext).isNotEqualTo(context); + assertThat(mWifiStateWorker).isEqualTo(instance); + } + + @Test + public void getWifiState_wifiStateDisabling_returnWifiStateDisabling() { + when(mWifiManager.getWifiState()).thenReturn(WIFI_STATE_DISABLING); + WifiStateWorker.refresh(); + + assertThat(mWifiStateWorker.getWifiState()).isEqualTo(WIFI_STATE_DISABLING); + } + + @Test + public void getWifiState_wifiStateDisabled_returnWifiStateDisabled() { + when(mWifiManager.getWifiState()).thenReturn(WIFI_STATE_DISABLED); + WifiStateWorker.refresh(); + + assertThat(mWifiStateWorker.getWifiState()).isEqualTo(WIFI_STATE_DISABLED); + } + + @Test + public void getWifiState_wifiStateEnabling_returnWifiStateEnabling() { + when(mWifiManager.getWifiState()).thenReturn(WIFI_STATE_ENABLING); + WifiStateWorker.refresh(); + + assertThat(mWifiStateWorker.getWifiState()).isEqualTo(WIFI_STATE_ENABLING); + } + + @Test + public void getWifiState_wifiStateEnabled_returnWifiStateEnabled() { + when(mWifiManager.getWifiState()).thenReturn(WIFI_STATE_ENABLED); + WifiStateWorker.refresh(); + + assertThat(mWifiStateWorker.getWifiState()).isEqualTo(WIFI_STATE_ENABLED); + } + + @Test + public void isWifiEnabled_wifiStateDisabling_returnFalse() { + when(mWifiManager.getWifiState()).thenReturn(WIFI_STATE_DISABLING); + WifiStateWorker.refresh(); + + assertThat(mWifiStateWorker.isWifiEnabled()).isFalse(); + } + + @Test + public void isWifiEnabled_wifiStateDisabled_returnFalse() { + when(mWifiManager.getWifiState()).thenReturn(WIFI_STATE_DISABLED); + WifiStateWorker.refresh(); + + assertThat(mWifiStateWorker.isWifiEnabled()).isFalse(); + } + + @Test + public void isWifiEnabled_wifiStateEnabling_returnFalse() { + when(mWifiManager.getWifiState()).thenReturn(WIFI_STATE_ENABLING); + WifiStateWorker.refresh(); + + assertThat(mWifiStateWorker.isWifiEnabled()).isFalse(); + } + + @Test + public void isWifiEnabled_wifiStateEnabled_returnTrue() { + when(mWifiManager.getWifiState()).thenReturn(WIFI_STATE_ENABLED); + WifiStateWorker.refresh(); + + assertThat(mWifiStateWorker.isWifiEnabled()).isTrue(); + } +}