diff --git a/src/com/android/settings/wifi/WifiSwitchPreference.kt b/src/com/android/settings/wifi/WifiSwitchPreference.kt index 95497546727..0eef999ff8e 100644 --- a/src/com/android/settings/wifi/WifiSwitchPreference.kt +++ b/src/com/android/settings/wifi/WifiSwitchPreference.kt @@ -30,6 +30,8 @@ import com.android.settings.PreferenceRestrictionMixin import com.android.settings.R import com.android.settings.network.SatelliteRepository.Companion.isSatelliteOn import com.android.settings.network.SatelliteWarningDialogActivity +import com.android.settings.wifi.utils.isWifiEnabled +import com.android.settings.wifi.utils.wifiManager import com.android.settingslib.RestrictedSwitchPreference import com.android.settingslib.WirelessUtils import com.android.settingslib.datastore.AbstractKeyedDataObservable @@ -118,16 +120,14 @@ class WifiSwitchPreference : private var broadcastReceiver: BroadcastReceiver? = null - override fun contains(key: String) = - key == KEY && context.getSystemService(WifiManager::class.java) != null + override fun contains(key: String) = key == KEY && context.wifiManager != null override fun getValue(key: String, valueType: Class): T? = - context.getSystemService(WifiManager::class.java)?.isWifiEnabled as T? + context.isWifiEnabled as T? - @Suppress("DEPRECATION") override fun setValue(key: String, valueType: Class, value: T?) { if (value is Boolean) { - context.getSystemService(WifiManager::class.java)?.isWifiEnabled = value + context.isWifiEnabled = value } } diff --git a/src/com/android/settings/wifi/tether/WifiHotspotSwitchPreference.kt b/src/com/android/settings/wifi/tether/WifiHotspotSwitchPreference.kt index 2f72c4a6fb8..c581b76cd89 100644 --- a/src/com/android/settings/wifi/tether/WifiHotspotSwitchPreference.kt +++ b/src/com/android/settings/wifi/tether/WifiHotspotSwitchPreference.kt @@ -33,6 +33,10 @@ import com.android.settings.Utils import com.android.settings.core.SubSettingLauncher import com.android.settings.datausage.DataSaverMainSwitchPreference.Companion.KEY as DATA_SAVER_KEY import com.android.settings.wifi.WifiUtils.canShowWifiHotspot +import com.android.settings.wifi.utils.tetheringManager +import com.android.settings.wifi.utils.wifiApState +import com.android.settings.wifi.utils.wifiManager +import com.android.settings.wifi.utils.wifiSoftApSsid import com.android.settingslib.PrimarySwitchPreference import com.android.settingslib.TetherUtil import com.android.settingslib.datastore.AbstractKeyedDataObservable @@ -50,7 +54,6 @@ import com.android.settingslib.preference.PreferenceBinding import com.android.settingslib.wifi.WifiUtils.Companion.getWifiTetherSummaryForConnectedDevices // LINT.IfChange -@Suppress("MissingPermission", "NewApi", "UNCHECKED_CAST") class WifiHotspotSwitchPreference(context: Context, dataSaverStore: KeyValueStore) : SwitchPreference(KEY, R.string.wifi_hotspot_checkbox_text), PreferenceBinding, @@ -66,14 +69,14 @@ class WifiHotspotSwitchPreference(context: Context, dataSaverStore: KeyValueStor !Utils.isMonkeyRunning() override fun getSummary(context: Context): CharSequence? = - when (context.wifiManager?.wifiApState) { + when (context.wifiApState) { WifiManager.WIFI_AP_STATE_ENABLING -> context.getString(R.string.wifi_tether_starting) WifiManager.WIFI_AP_STATE_ENABLED -> { val sapClientsSize = wifiHotspotStore.sapClientsSize if (sapClientsSize == null) { context.getString( R.string.wifi_tether_enabled_subtext, - BidiFormatter.getInstance().unicodeWrap(context.wifiSsid), + BidiFormatter.getInstance().unicodeWrap(context.wifiSoftApSsid), ) } else { getWifiTetherSummaryForConnectedDevices(context, sapClientsSize) @@ -123,6 +126,7 @@ class WifiHotspotSwitchPreference(context: Context, dataSaverStore: KeyValueStor override fun storage(context: Context): KeyValueStore = wifiHotspotStore + @Suppress("UNCHECKED_CAST") private class WifiHotspotStore( private val context: Context, val dataSaverStore: KeyValueStore, @@ -141,7 +145,7 @@ class WifiHotspotSwitchPreference(context: Context, dataSaverStore: KeyValueStor key == KEY && context.wifiManager != null && context.tetheringManager != null override fun getValue(key: String, valueType: Class): T? { - val wifiApState = context.wifiManager?.wifiApState + val wifiApState = context.wifiApState val value = wifiApState == WifiManager.WIFI_AP_STATE_ENABLING || wifiApState == WifiManager.WIFI_AP_STATE_ENABLED @@ -204,16 +208,6 @@ class WifiHotspotSwitchPreference(context: Context, dataSaverStore: KeyValueStor companion object { const val TAG = "WifiHotspotSwitchPreference" const val KEY = "wifi_tether" - - private val Context.wifiManager: WifiManager? - get() = applicationContext.getSystemService(WifiManager::class.java) - - @Suppress("DEPRECATION") - private val Context.wifiSsid - get() = wifiManager?.softApConfiguration?.ssid - - private val Context.tetheringManager: TetheringManager? - get() = applicationContext.getSystemService(TetheringManager::class.java) } } // LINT.ThenChange(WifiTetherPreferenceController.java) diff --git a/src/com/android/settings/wifi/utils/Contexts.kt b/src/com/android/settings/wifi/utils/Contexts.kt new file mode 100644 index 00000000000..42a6b98114b --- /dev/null +++ b/src/com/android/settings/wifi/utils/Contexts.kt @@ -0,0 +1,54 @@ +/* + * Copyright (C) 2024 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. + */ + +@file:Suppress("DEPRECATION", "MissingPermission") + +package com.android.settings.wifi.utils + +import android.content.Context +import android.net.TetheringManager +import android.net.wifi.WifiManager + +/** + * Gets the {@link android.net.wifi.WifiManager} system service. + * + * Use application context to get system services to avoid memory leaks. + */ +val Context.wifiManager: WifiManager? + get() = applicationContext.getSystemService(WifiManager::class.java) + +/** Return the UTF-8 String set to be the SSID for the Soft AP. */ +val Context.wifiSoftApSsid + get() = wifiManager?.softApConfiguration?.ssid + +/** Gets the tethered Wi-Fi hotspot enabled state. */ +val Context.wifiApState + get() = wifiManager?.wifiApState + +/** Gets/Sets the Wi-Fi enabled state. */ +var Context.isWifiEnabled: Boolean + get() = wifiManager?.isWifiEnabled == true + set(value) { + wifiManager?.isWifiEnabled = value + } + +/** + * Gets the {@link android.net.TetheringManager} system service. + * + * Use application context to get system services to avoid memory leaks. + */ +val Context.tetheringManager: TetheringManager? + get() = applicationContext.getSystemService(TetheringManager::class.java) diff --git a/tests/unit/src/com/android/settings/wifi/WifiSwitchPreferenceTest.kt b/tests/unit/src/com/android/settings/wifi/WifiSwitchPreferenceTest.kt index 41de0901bd5..ca2ae8eb1cb 100644 --- a/tests/unit/src/com/android/settings/wifi/WifiSwitchPreferenceTest.kt +++ b/tests/unit/src/com/android/settings/wifi/WifiSwitchPreferenceTest.kt @@ -32,10 +32,12 @@ import org.mockito.kotlin.stub @RunWith(AndroidJUnit4::class) class WifiSwitchPreferenceTest { - private var mockWifiManager = mock() + private val mockWifiManager = mock() private val context = object : ContextWrapper(ApplicationProvider.getApplicationContext()) { + override fun getApplicationContext() = this + override fun getSystemService(name: String): Any? = when (name) { getSystemServiceName(WifiManager::class.java) -> mockWifiManager diff --git a/tests/unit/src/com/android/settings/wifi/tether/WifiHotspotSwitchPreferenceTest.kt b/tests/unit/src/com/android/settings/wifi/tether/WifiHotspotSwitchPreferenceTest.kt new file mode 100644 index 00000000000..fad8025d71b --- /dev/null +++ b/tests/unit/src/com/android/settings/wifi/tether/WifiHotspotSwitchPreferenceTest.kt @@ -0,0 +1,89 @@ +/* + * Copyright (C) 2024 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.settings.wifi.tether + +import android.content.ContextWrapper +import android.net.TetheringManager +import android.net.TetheringManager.TETHERING_WIFI +import android.net.wifi.WifiManager +import android.net.wifi.WifiManager.WIFI_AP_STATE_DISABLED +import android.net.wifi.WifiManager.WIFI_AP_STATE_ENABLED +import androidx.test.core.app.ApplicationProvider +import androidx.test.ext.junit.runners.AndroidJUnit4 +import com.android.settingslib.datastore.KeyValueStore +import com.google.common.truth.Truth.assertThat +import org.junit.Test +import org.junit.runner.RunWith +import org.mockito.ArgumentMatchers.eq +import org.mockito.Mockito.verify +import org.mockito.kotlin.anyVararg +import org.mockito.kotlin.doReturn +import org.mockito.kotlin.mock +import org.mockito.kotlin.stub + +@RunWith(AndroidJUnit4::class) +class WifiHotspotSwitchPreferenceTest { + private val mockWifiManager = mock() + private val mockTetheringManager = mock() + private val mockDataSaverStore = mock() + + private val context = + object : ContextWrapper(ApplicationProvider.getApplicationContext()) { + override fun getApplicationContext() = this + + override fun getSystemService(name: String): Any? = + when (name) { + getSystemServiceName(WifiManager::class.java) -> mockWifiManager + getSystemServiceName(TetheringManager::class.java) -> mockTetheringManager + else -> super.getSystemService(name) + } + } + + private val preference = WifiHotspotSwitchPreference(context, mockDataSaverStore) + + @Test + fun getValue_defaultOn_returnOn() { + mockWifiManager.stub { on { wifiApState } doReturn WIFI_AP_STATE_ENABLED } + + val getValue = preference.storage(context).getBoolean(WifiHotspotSwitchPreference.KEY) + + assertThat(getValue).isTrue() + } + + @Test + fun getValue_defaultOff_returnOff() { + mockWifiManager.stub { on { wifiApState } doReturn WIFI_AP_STATE_DISABLED } + + val getValue = preference.storage(context).getBoolean(WifiHotspotSwitchPreference.KEY) + + assertThat(getValue).isFalse() + } + + @Test + fun setValue_valueOn_startTethering() { + preference.storage(context).setBoolean(WifiHotspotSwitchPreference.KEY, true) + + verify(mockTetheringManager).startTethering(eq(TETHERING_WIFI), anyVararg(), anyVararg()) + } + + @Test + fun setValue_valueOff_stopTethering() { + preference.storage(context).setBoolean(WifiHotspotSwitchPreference.KEY, false) + + verify(mockTetheringManager).stopTethering(eq(TETHERING_WIFI)) + } +}