From 6fb75678e5091e66a675182cf61e6adf596e7835 Mon Sep 17 00:00:00 2001 From: Weng Su Date: Tue, 3 Dec 2024 04:03:06 +0000 Subject: [PATCH] [Catalyst] Migrate Wi-Fi Hotspot preference Bug: 368359963 Flag: com.android.settings.flags.catalyst_tether_settings Test: Manual testing atest -c TetherSettingsTest \ TetherScreenTest Change-Id: I89d418454af7887a4892c616f4efff481b536a91 --- res/xml/tether_prefs.xml | 3 +- .../settings/network/tether/TetherScreen.kt | 5 +- .../network/tether/TetherSettings.java | 14 +- .../tether/WifiHotspotSwitchPreference.kt | 241 ++++++++++++++++++ .../WifiTetherPreferenceController.java | 2 + .../network/tether/TetherScreenTest.kt | 3 + 6 files changed, 261 insertions(+), 7 deletions(-) create mode 100644 src/com/android/settings/wifi/tether/WifiHotspotSwitchPreference.kt diff --git a/res/xml/tether_prefs.xml b/res/xml/tether_prefs.xml index 89bd631bcd2..c3ea3cbcd8b 100644 --- a/res/xml/tether_prefs.xml +++ b/res/xml/tether_prefs.xml @@ -28,8 +28,7 @@ android:title="@string/wifi_hotspot_checkbox_text" android:summary="@string/wifi_hotspot_off_subtext" android:fragment="com.android.settings.wifi.tether.WifiTetherSettings" - settings:allowDividerAbove="true" - settings:maxLines="2"/> + settings:allowDividerAbove="true"/> context.getString(R.string.wifi_tether_starting) + WifiManager.WIFI_AP_STATE_ENABLED -> + when (wifiHotspotStore.sapClientsSize) { + null -> + context.getString( + R.string.wifi_tether_enabled_subtext, + BidiFormatter.getInstance() + .unicodeWrap(context.getSoftApConfiguration()?.ssid), + ) + else -> + getWifiTetherSummaryForConnectedDevices( + context, + wifiHotspotStore.sapClientsSize!!, + ) + } + WifiManager.WIFI_AP_STATE_DISABLING -> context.getString(R.string.wifi_tether_stopping) + WifiManager.WIFI_AP_STATE_DISABLED -> + context.getString(R.string.wifi_hotspot_off_subtext) + else -> + when (wifiHotspotStore.sapFailureReason) { + WifiManager.SAP_START_FAILURE_NO_CHANNEL -> + context.getString(R.string.wifi_sap_no_channel_error) + else -> context.getString(R.string.wifi_error) + } + } + + override fun intent(context: Context): Intent? = + SubSettingLauncher(context) + .apply { + setDestination(WifiTetherSettings::class.java.name) + setTitleRes(R.string.wifi_hotspot_checkbox_text) + setSourceMetricsCategory(SettingsEnums.WIFI_TETHER_SETTINGS) + } + .toIntent() + + override fun isEnabled(context: Context) = + !dataSaverBackend.isDataSaverEnabled && super.isEnabled(context) + + override val restrictionKeys + get() = arrayOf(UserManager.DISALLOW_WIFI_TETHERING) + + override fun getReadPermit(context: Context, myUid: Int, callingUid: Int) = + ReadWritePermit.ALLOW + + override fun getWritePermit(context: Context, value: Boolean?, myUid: Int, callingUid: Int) = + when { + dataSaverBackend.isDataSaverEnabled -> ReadWritePermit.DISALLOW + else -> ReadWritePermit.ALLOW + } + + override val sensitivityLevel + get() = SensitivityLevel.HIGH_SENSITIVITY + + override fun createWidget(context: Context) = PrimarySwitchPreference(context) + + override fun storage(context: Context): KeyValueStore = wifiHotspotStore + + private class WifiHotspotStore(private val context: Context) : + AbstractKeyedDataObservable(), KeyValueStore { + + private var wifiTetherSoftApManager: WifiTetherSoftApManager? = null + var sapState: Int = WifiManager.WIFI_AP_STATE_DISABLED + var sapFailureReason: Int? = null + var sapClientsSize: Int? = null + + override fun contains(key: String) = + key == KEY && context.wifiManager != null && context.tetheringManager != null + + override fun getValue(key: String, valueType: Class): T? { + val wifiApState = context.wifiManager?.wifiApState + val value = + wifiApState == WifiManager.WIFI_AP_STATE_ENABLING || + wifiApState == WifiManager.WIFI_AP_STATE_ENABLED + return value as T? + } + + override fun setValue(key: String, valueType: Class, value: T?) { + if (value !is Boolean) return + context.tetheringManager?.let { + if (value) { + val startTetheringCallback = + object : TetheringManager.StartTetheringCallback { + override fun onTetheringStarted() { + Log.d(TAG, "onTetheringStarted()") + } + + override fun onTetheringFailed(error: Int) { + Log.e(TAG, "onTetheringFailed(),error=$error") + } + } + it.startTethering( + TetheringManager.TETHERING_WIFI, + HandlerExecutor.main, + startTetheringCallback, + ) + } else { + it.stopTethering(TetheringManager.TETHERING_WIFI) + } + } + } + + override fun onFirstObserverAdded() { + val wifiSoftApCallback = + object : WifiTetherSoftApManager.WifiTetherSoftApCallback { + override fun onStateChanged(state: Int, failureReason: Int) { + Log.d(TAG, "onStateChanged(),state=$state,failureReason=$failureReason") + sapState = state + sapFailureReason = failureReason + if (state == WifiManager.WIFI_AP_STATE_DISABLED) sapClientsSize = null + notifyChange(KEY, DataChangeReason.UPDATE) + } + + override fun onConnectedClientsChanged(clients: List?) { + sapClientsSize = clients?.size ?: 0 + Log.d(TAG, "onConnectedClientsChanged(),sapClientsSize=$sapClientsSize") + notifyChange(KEY, DataChangeReason.UPDATE) + } + } + wifiTetherSoftApManager = + WifiTetherSoftApManager(context.wifiManager, wifiSoftApCallback) + wifiTetherSoftApManager?.registerSoftApCallback() + } + + override fun onLastObserverRemoved() { + wifiTetherSoftApManager?.unRegisterSoftApCallback() + } + } + + override fun bind(preference: Preference, metadata: PreferenceMetadata) { + super.bind(preference, metadata) + (preference as PrimarySwitchPreference).apply { + isChecked = preferenceDataStore!!.getBoolean(key, false) + } + } + + override fun onStart(context: PreferenceLifecycleContext) { + val listener = + DataSaverBackend.Listener { isDataSaving: Boolean -> + context.findPreference(KEY)?.isSwitchEnabled = + !isDataSaving + context.notifyPreferenceChange(KEY) + } + dataSaverBackendListener = listener + dataSaverBackend.addListener(listener) + } + + override fun onStop(context: PreferenceLifecycleContext) { + dataSaverBackendListener?.let { + dataSaverBackend.remListener(it) + dataSaverBackendListener = null + } + } + + companion object { + const val TAG = "WifiHotspotSwitchPreference" + const val KEY = "wifi_tether" + + private val Context.wifiManager: WifiManager? + get() = applicationContext.getSystemService(WifiManager::class.java) + + private fun Context.getSoftApConfiguration() = wifiManager?.softApConfiguration + + private val Context.tetheringManager: TetheringManager? + get() = applicationContext.getSystemService(TetheringManager::class.java) + } +} +// LINT.ThenChange(WifiTetherPreferenceController.java) diff --git a/src/com/android/settings/wifi/tether/WifiTetherPreferenceController.java b/src/com/android/settings/wifi/tether/WifiTetherPreferenceController.java index 0baac2c063a..d5d075106c5 100644 --- a/src/com/android/settings/wifi/tether/WifiTetherPreferenceController.java +++ b/src/com/android/settings/wifi/tether/WifiTetherPreferenceController.java @@ -48,6 +48,7 @@ import com.android.settingslib.wifi.WifiUtils; import java.util.List; +// LINT.IfChange public class WifiTetherPreferenceController extends AbstractPreferenceController implements PreferenceControllerMixin, LifecycleObserver, OnStart, OnStop, SwitchWidgetController.OnSwitchChangeListener { @@ -251,3 +252,4 @@ public class WifiTetherPreferenceController extends AbstractPreferenceController return true; } } +// LINT.ThenChange(WifiHotspotSwitchPreference.kt) diff --git a/tests/robotests/src/com/android/settings/network/tether/TetherScreenTest.kt b/tests/robotests/src/com/android/settings/network/tether/TetherScreenTest.kt index 0eeac4353f1..f3d1d394e30 100644 --- a/tests/robotests/src/com/android/settings/network/tether/TetherScreenTest.kt +++ b/tests/robotests/src/com/android/settings/network/tether/TetherScreenTest.kt @@ -40,6 +40,9 @@ class TetherScreenTest : CatalystScreenTestCase() { override val flagName: String get() = Flags.FLAG_CATALYST_TETHER_SETTINGS + // TODO: Remove override (See b/368359963#comment7) + override fun migration() {} + @Before fun setUp() { ShadowConnectivityManager.getShadow().setTetheringSupported(true)