Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit bf0e8c1d authored by Weng Su's avatar Weng Su
Browse files

Restart Wi-Fi tethering automatically if configuration change

- When the Wi-Fi Hotspot is already started, if the user changes the configuration, the Wi-Fi Hotspot will be restarted automatically.

- When the Wi-Fi hotspot restarts, display a circle on the screen to indicate that it is processing.

Bug: 245258763
Test: manual test
atest -c WifiTetherSettingsTest \
         WifiHotspotSpeedSettingsTest
atest -c WifiHotspotRepositoryTest \
         WifiHotspotSecuritySettingsTest \
         WifiHotspotSecurityViewModelTest \
         WifiHotspotSpeedViewModelTest \
         WifiTetherViewModelTest

Change-Id: I6fdd5892916703095f28d0589ebc3b7dd59fcd61
parent 0a506448
Loading
Loading
Loading
Loading
+21 −7
Original line number Diff line number Diff line
@@ -18,6 +18,7 @@ package com.android.settings.wifi.factory;

import android.annotation.Nullable;
import android.content.Context;
import android.net.TetheringManager;
import android.net.wifi.WifiManager;
import android.util.Log;

@@ -40,6 +41,7 @@ public class WifiFeatureProvider {

    private final Context mAppContext;
    private WifiManager mWifiManager;
    private TetheringManager mTetheringManager;
    private WifiVerboseLogging mWifiVerboseLogging;
    private WifiHotspotRepository mWifiHotspotRepository;

@@ -48,7 +50,7 @@ public class WifiFeatureProvider {
    }

    /**
     * Get WifiManager
     * Gets WifiManager
     */
    public WifiManager getWifiManager() {
        if (mWifiManager == null) {
@@ -58,7 +60,18 @@ public class WifiFeatureProvider {
    }

    /**
     * Get WifiVerboseLogging
     * Gets TetheringManager
     */
    public TetheringManager getTetheringManager() {
        if (mTetheringManager == null) {
            mTetheringManager = mAppContext.getSystemService(TetheringManager.class);
            verboseLog(TAG, "getTetheringManager():" + mTetheringManager);
        }
        return mTetheringManager;
    }

    /**
     * Gets WifiVerboseLogging
     */
    public WifiVerboseLogging getWifiVerboseLogging() {
        if (mWifiVerboseLogging == null) {
@@ -68,25 +81,26 @@ public class WifiFeatureProvider {
    }

    /**
     * Get WifiHotspotRepository
     * Gets WifiHotspotRepository
     */
    public WifiHotspotRepository getWifiHotspotRepository() {
        if (mWifiHotspotRepository == null) {
            mWifiHotspotRepository = new WifiHotspotRepository(mAppContext, getWifiManager());
            mWifiHotspotRepository = new WifiHotspotRepository(mAppContext, getWifiManager(),
                    getTetheringManager());
            verboseLog(TAG, "getWifiHotspotRepository():" + mWifiHotspotRepository);
        }
        return mWifiHotspotRepository;
    }

    /**
     * Get WifiTetherViewModel
     * Gets WifiTetherViewModel
     */
    public WifiTetherViewModel getWifiTetherViewModel(@NotNull ViewModelStoreOwner owner) {
        return new ViewModelProvider(owner).get(WifiTetherViewModel.class);
    }

    /**
     * Get WifiHotspotSecurityViewModel
     * Gets WifiHotspotSecurityViewModel
     */
    public WifiHotspotSecurityViewModel getWifiHotspotSecurityViewModel(
            @NotNull ViewModelStoreOwner owner) {
@@ -97,7 +111,7 @@ public class WifiFeatureProvider {
    }

    /**
     * Get WifiHotspotSpeedViewModel
     * Gets WifiHotspotSpeedViewModel
     */
    public WifiHotspotSpeedViewModel getWifiHotspotSpeedViewModel(
            @NotNull ViewModelStoreOwner owner) {
+128 −11
Original line number Diff line number Diff line
@@ -16,14 +16,18 @@

package com.android.settings.wifi.repository;

import static android.net.TetheringManager.TETHERING_WIFI;
import static android.net.wifi.SoftApConfiguration.BAND_2GHZ;
import static android.net.wifi.SoftApConfiguration.BAND_5GHZ;
import static android.net.wifi.SoftApConfiguration.BAND_6GHZ;
import static android.net.wifi.SoftApConfiguration.SECURITY_TYPE_OPEN;
import static android.net.wifi.SoftApConfiguration.SECURITY_TYPE_WPA3_SAE;
import static android.net.wifi.WifiAvailableChannel.OP_MODE_SAP;
import static android.net.wifi.WifiManager.WIFI_AP_STATE_DISABLED;
import static android.net.wifi.WifiManager.WIFI_AP_STATE_ENABLED;

import android.content.Context;
import android.net.TetheringManager;
import android.net.wifi.SoftApConfiguration;
import android.net.wifi.WifiAvailableChannel;
import android.net.wifi.WifiManager;
@@ -51,6 +55,8 @@ import java.util.function.Consumer;
public class WifiHotspotRepository {
    private static final String TAG = "WifiHotspotRepository";

    private static final int RESTART_INTERVAL_MS = 100;

    /** Wi-Fi hotspot band unknown. */
    public static final int BAND_UNKNOWN = 0;
    /** Wi-Fi hotspot band 2.4GHz and 5GHz. */
@@ -79,8 +85,9 @@ public class WifiHotspotRepository {
        sSpeedMap.put(BAND_2GHZ_5GHZ, SPEED_2GHZ_5GHZ);
    }

    protected final Context mAppContext;
    protected final WifiManager mWifiManager;
    private final Context mAppContext;
    private final WifiManager mWifiManager;
    private final TetheringManager mTetheringManager;

    protected String mLastPassword;
    protected LastPasswordListener mLastPasswordListener = new LastPasswordListener();
@@ -102,9 +109,24 @@ public class WifiHotspotRepository {
    Boolean mIsConfigShowSpeed;
    private Boolean mIsSpeedFeatureAvailable;

    public WifiHotspotRepository(@NonNull Context appContext, @NonNull WifiManager wifiManager) {
    @VisibleForTesting
    SoftApCallback mSoftApCallback = new SoftApCallback();
    @VisibleForTesting
    StartTetheringCallback mStartTetheringCallback;
    @VisibleForTesting
    int mWifiApState = WIFI_AP_STATE_DISABLED;

    @VisibleForTesting
    boolean mIsRestarting;
    @VisibleForTesting
    MutableLiveData<Boolean> mRestarting;

    public WifiHotspotRepository(@NonNull Context appContext, @NonNull WifiManager wifiManager,
            @NonNull TetheringManager tetheringManager) {
        mAppContext = appContext;
        mWifiManager = wifiManager;
        mTetheringManager = tetheringManager;
        mWifiManager.registerSoftApCallback(mAppContext.getMainExecutor(), mSoftApCallback);
    }

    /**
@@ -126,6 +148,15 @@ public class WifiHotspotRepository {
        return !TextUtils.isEmpty(mLastPassword) ? mLastPassword : generateRandomPassword();
    }

    @VisibleForTesting
    String generatePassword(SoftApConfiguration config) {
        String password = config.getPassphrase();
        if (TextUtils.isEmpty(password)) {
            password = generatePassword();
        }
        return password;
    }

    private class LastPasswordListener implements Consumer<String> {
        @Override
        public void accept(String password) {
@@ -139,14 +170,28 @@ public class WifiHotspotRepository {
        return randomUUID.substring(0, 8) + randomUUID.substring(9, 13);
    }

    /**
     * Gets the Wi-Fi tethered AP Configuration.
     *
     * @return AP details in {@link SoftApConfiguration}
     */
    public SoftApConfiguration getSoftApConfiguration() {
        return mWifiManager.getSoftApConfiguration();
    }

    /**
     * Sets the tethered Wi-Fi AP Configuration.
     *
     * @param config A valid SoftApConfiguration specifying the configuration of the SAP.
     */
    public void setSoftApConfiguration(@NonNull SoftApConfiguration config) {
        if (mIsRestarting) {
            Log.e(TAG, "Skip setSoftApConfiguration because hotspot is restarting.");
            return;
        }
        mWifiManager.setSoftApConfiguration(config);
        refresh();
        restartTetheringIfNeeded();
    }

    /**
@@ -217,13 +262,7 @@ public class WifiHotspotRepository {
            return;
        }
        SoftApConfiguration.Builder configBuilder = new SoftApConfiguration.Builder(config);
        String passphrase = null;
        if (securityType != SECURITY_TYPE_OPEN) {
            passphrase = config.getPassphrase();
            if (TextUtils.isEmpty(passphrase)) {
                passphrase = generatePassword();
            }
        }
        String passphrase = (securityType == SECURITY_TYPE_OPEN) ? null : generatePassword(config);
        configBuilder.setPassphrase(passphrase, securityType);
        setSoftApConfiguration(configBuilder.build());

@@ -302,7 +341,7 @@ public class WifiHotspotRepository {
            configBuilder.setBand(BAND_2GHZ_5GHZ_6GHZ);
            if (config.getSecurityType() != SECURITY_TYPE_WPA3_SAE) {
                log("setSpeedType(), setPassphrase(SECURITY_TYPE_WPA3_SAE)");
                configBuilder.setPassphrase(generatePassword(), SECURITY_TYPE_WPA3_SAE);
                configBuilder.setPassphrase(generatePassword(config), SECURITY_TYPE_WPA3_SAE);
            }
        } else if (speedType == SPEED_5GHZ) {
            log("setSpeedType(), setBand(BAND_2GHZ_5GHZ)");
@@ -543,6 +582,84 @@ public class WifiHotspotRepository {
        }
    }

    /**
     * Gets Restarting LiveData
     */
    public LiveData<Boolean> getRestarting() {
        if (mRestarting == null) {
            mRestarting = new MutableLiveData<>();
            mRestarting.setValue(mIsRestarting);
        }
        return mRestarting;
    }

    private void setRestarting(boolean isRestarting) {
        log("setRestarting(), isRestarting:" + isRestarting);
        mIsRestarting = isRestarting;
        if (mRestarting != null) {
            mRestarting.setValue(mIsRestarting);
        }
    }

    @VisibleForTesting
    void restartTetheringIfNeeded() {
        if (mWifiApState != WIFI_AP_STATE_ENABLED) {
            return;
        }
        log("restartTetheringIfNeeded()");
        mAppContext.getMainThreadHandler().postDelayed(() -> {
            setRestarting(true);
            stopTethering();
        }, RESTART_INTERVAL_MS);
    }

    private void startTethering() {
        if (mStartTetheringCallback == null) {
            mStartTetheringCallback = new StartTetheringCallback();
        }
        log("startTethering()");
        mTetheringManager.startTethering(TETHERING_WIFI, mAppContext.getMainExecutor(),
                mStartTetheringCallback);
    }

    private void stopTethering() {
        log("startTethering()");
        mTetheringManager.stopTethering(TETHERING_WIFI);
    }

    @VisibleForTesting
    class SoftApCallback implements WifiManager.SoftApCallback {
        @Override
        public void onStateChanged(int state, int failureReason) {
            log("onStateChanged(), state:" + state + ", failureReason:" + failureReason);
            mWifiApState = state;
            if (!mIsRestarting) {
                return;
            }
            if (state == WIFI_AP_STATE_DISABLED) {
                mAppContext.getMainThreadHandler().postDelayed(() -> startTethering(),
                        RESTART_INTERVAL_MS);
                return;
            }
            if (state == WIFI_AP_STATE_ENABLED) {
                refresh();
                setRestarting(false);
            }
        }
    }

    private class StartTetheringCallback implements TetheringManager.StartTetheringCallback {
        @Override
        public void onTetheringStarted() {
            log("onTetheringStarted()");
        }

        @Override
        public void onTetheringFailed(int error) {
            log("onTetheringFailed(), error:" + error);
        }
    }

    private void log(String msg) {
        FeatureFactory.getFactory(mAppContext).getWifiFeatureProvider().verboseLog(TAG, msg);
    }
+8 −0
Original line number Diff line number Diff line
@@ -19,6 +19,7 @@ package com.android.settings.wifi.tether;
import android.app.settings.SettingsEnums;
import android.os.Bundle;

import androidx.annotation.VisibleForTesting;
import androidx.lifecycle.LiveData;

import com.android.settings.R;
@@ -72,6 +73,7 @@ public class WifiHotspotSecuritySettings extends DashboardFragment implements
            SelectorWithWidgetPreference preference = findPreference(viewItem.mKey);
            preference.setOnClickListener(this);
        }
        mWifiHotspotSecurityViewModel.getRestarting().observe(this, this::onRestartingChanged);
    }

    protected void onViewItemListDataChanged(
@@ -96,6 +98,12 @@ public class WifiHotspotSecuritySettings extends DashboardFragment implements
        }
    }

    @VisibleForTesting
    void onRestartingChanged(Boolean restarting) {
        log("onRestartingChanged(), restarting:" + restarting);
        setLoading(restarting, false);
    }

    @Override
    public void onRadioButtonClicked(SelectorWithWidgetPreference emiter) {
        String key = emiter.getKey();
+7 −0
Original line number Diff line number Diff line
@@ -129,6 +129,13 @@ public class WifiHotspotSecurityViewModel extends AndroidViewModel {
        mViewInfoListData.setValue(mViewItemMap.values().stream().toList());
    }

    /**
     * Gets Restarting LiveData
     */
    public LiveData<Boolean> getRestarting() {
        return mWifiHotspotRepository.getRestarting();
    }

    /**
     * Wi-Fi Hotspot View Item
     */
+9 −0
Original line number Diff line number Diff line
@@ -24,6 +24,8 @@ import static com.android.settings.wifi.repository.WifiHotspotRepository.SPEED_6
import android.app.settings.SettingsEnums;
import android.os.Bundle;

import androidx.annotation.VisibleForTesting;

import com.android.settings.R;
import com.android.settings.dashboard.DashboardFragment;
import com.android.settings.overlay.FeatureFactory;
@@ -80,6 +82,7 @@ public class WifiHotspotSpeedSettings extends DashboardFragment implements
        onSpeedInfoMapDataChanged(mWifiHotspotSpeedViewModel.getSpeedInfoMapData().getValue());
        mWifiHotspotSpeedViewModel.getSpeedInfoMapData()
                .observe(this, this::onSpeedInfoMapDataChanged);
        mWifiHotspotSpeedViewModel.getRestarting().observe(this, this::onRestartingChanged);
    }

    protected void loadPreferences() {
@@ -117,6 +120,12 @@ public class WifiHotspotSpeedSettings extends DashboardFragment implements
        }
    }

    @VisibleForTesting
    void onRestartingChanged(Boolean restarting) {
        log("onRestartingChanged(), restarting:" + restarting);
        setLoading(restarting, false);
    }

    @Override
    public void onRadioButtonClicked(SelectorWithWidgetPreference emiter) {
        String key = emiter.getKey();
Loading