Loading src/com/android/settings/network/InternetResetHelper.java +115 −97 Original line number Diff line number Diff line Loading @@ -21,14 +21,9 @@ 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.text.TextUtils; import android.util.Log; import androidx.annotation.UiThread; import androidx.annotation.VisibleForTesting; import androidx.annotation.WorkerThread; import androidx.lifecycle.Lifecycle; import androidx.lifecycle.LifecycleObserver; import androidx.lifecycle.OnLifecycleEvent; Loading @@ -38,14 +33,14 @@ import androidx.preference.PreferenceCategory; import com.android.settingslib.connectivity.ConnectivitySubsystemsRecoveryManager; import com.android.settingslib.utils.HandlerInjector; import java.lang.ref.WeakReference; import java.util.ArrayList; import java.util.List; /** * Helper class to restart connectivity for all requested subsystems. */ public class InternetResetHelper implements LifecycleObserver, ConnectivitySubsystemsRecoveryManager.RecoveryStatusCallback { public class InternetResetHelper implements LifecycleObserver { protected static final String TAG = "InternetResetHelper"; public static final long RESTART_TIMEOUT_MS = 15_000; // 15 seconds Loading @@ -61,41 +56,40 @@ public class InternetResetHelper implements LifecycleObserver, protected final IntentFilter mWifiStateFilter; protected final BroadcastReceiver mWifiStateReceiver = new BroadcastReceiver() { @Override @WorkerThread public void onReceive(Context context, Intent intent) { if (intent != null && TextUtils.equals(intent.getAction(), WifiManager.NETWORK_STATE_CHANGED_ACTION)) { updateWifiStateChange(); } } }; protected ConnectivitySubsystemsRecoveryManager mConnectivitySubsystemsRecoveryManager; protected HandlerThread mWorkerThread; protected boolean mIsRecoveryReady; protected boolean mIsWifiReady; protected RecoveryWorker mRecoveryWorker; protected boolean mIsWifiReady = true; protected HandlerInjector mHandlerInjector; protected final Runnable mResumeRunnable = () -> { resumePreferences(); }; protected final Runnable mTimeoutRunnable = () -> { mIsRecoveryReady = true; Log.w(TAG, "Resume preferences due to connectivity subsystems recovery timed out."); mRecoveryWorker.clearRecovering(); mIsWifiReady = true; resumePreferences(); }; public InternetResetHelper(Context context, Lifecycle lifecycle) { public InternetResetHelper(Context context, Lifecycle lifecycle, NetworkMobileProviderController mobileNetworkController, Preference wifiTogglePreferences, PreferenceCategory connectedWifiEntryPreferenceCategory, PreferenceCategory firstWifiEntryPreferenceCategory, PreferenceCategory wifiEntryPreferenceCategory, Preference resettingPreference) { mContext = context; mMobileNetworkController = mobileNetworkController; mWifiTogglePreferences = wifiTogglePreferences; mWifiNetworkPreferences.add(connectedWifiEntryPreferenceCategory); mWifiNetworkPreferences.add(firstWifiEntryPreferenceCategory); mWifiNetworkPreferences.add(wifiEntryPreferenceCategory); mResettingPreference = resettingPreference; mHandlerInjector = new HandlerInjector(context.getMainThreadHandler()); mWifiManager = mContext.getSystemService(WifiManager.class); mWifiStateFilter = new IntentFilter(WifiManager.NETWORK_STATE_CHANGED_ACTION); mWorkerThread = new HandlerThread(TAG + "{" + Integer.toHexString(System.identityHashCode(this)) + "}", Process.THREAD_PRIORITY_BACKGROUND); mWorkerThread.start(); mConnectivitySubsystemsRecoveryManager = new ConnectivitySubsystemsRecoveryManager( mContext, mWorkerThread.getThreadHandler()); mRecoveryWorker = RecoveryWorker.getInstance(mContext, this); if (lifecycle != null) { lifecycle.addObserver(this); Loading @@ -118,72 +112,18 @@ public class InternetResetHelper implements LifecycleObserver, /** @OnLifecycleEvent(Lifecycle.Event.ON_DESTROY) */ @OnLifecycleEvent(Lifecycle.Event.ON_DESTROY) public void onDestroy() { mHandlerInjector.removeCallbacks(mResumeRunnable); mHandlerInjector.removeCallbacks(mTimeoutRunnable); mWorkerThread.quit(); } @Override @WorkerThread public void onSubsystemRestartOperationBegin() { Log.d(TAG, "The connectivity subsystem is starting for recovery."); } @Override @WorkerThread public void onSubsystemRestartOperationEnd() { Log.d(TAG, "The connectivity subsystem is done for recovery."); if (!mIsRecoveryReady) { mIsRecoveryReady = true; mHandlerInjector.postDelayed(mResumeRunnable, 0 /* delayMillis */); } } @VisibleForTesting @WorkerThread protected void updateWifiStateChange() { if (!mIsWifiReady && mWifiManager.isWifiEnabled()) { Log.d(TAG, "The Wi-Fi subsystem is done for recovery."); mIsWifiReady = true; mHandlerInjector.postDelayed(mResumeRunnable, 0 /* delayMillis */); } } /** * Sets the resetting preference. */ @UiThread public void setResettingPreference(Preference preference) { mResettingPreference = preference; } /** * Sets the mobile network controller. */ @UiThread public void setMobileNetworkController(NetworkMobileProviderController controller) { mMobileNetworkController = controller; } /** * Sets the Wi-Fi toggle preference. */ @UiThread public void setWifiTogglePreference(Preference preference) { mWifiTogglePreferences = preference; } /** * Adds the Wi-Fi network preference. */ @UiThread public void addWifiNetworkPreference(PreferenceCategory preference) { if (preference != null) { mWifiNetworkPreferences.add(preference); resumePreferences(); } } @UiThread protected void suspendPreferences() { Log.d(TAG, "Suspend the subsystem preferences"); if (mMobileNetworkController != null) { Loading @@ -201,9 +141,9 @@ public class InternetResetHelper implements LifecycleObserver, } } @UiThread protected void resumePreferences() { if (mIsRecoveryReady && mMobileNetworkController != null) { boolean isRecoveryReady = !mRecoveryWorker.isRecovering(); if (isRecoveryReady && mMobileNetworkController != null) { Log.d(TAG, "Resume the Mobile Network controller"); mMobileNetworkController.hidePreference(false /* hide */, true /* immediately */); } Loading @@ -214,7 +154,7 @@ public class InternetResetHelper implements LifecycleObserver, pref.setVisible(true); } } if (mIsRecoveryReady && mIsWifiReady) { if (isRecoveryReady && mIsWifiReady) { mHandlerInjector.removeCallbacks(mTimeoutRunnable); if (mResettingPreference != null) { Log.d(TAG, "Resume the Resetting preference"); Loading @@ -223,21 +163,99 @@ public class InternetResetHelper implements LifecycleObserver, } } /** * Restart connectivity for all requested subsystems. */ @UiThread protected void showResettingAndSendTimeoutChecks() { suspendPreferences(); mHandlerInjector.postDelayed(mTimeoutRunnable, RESTART_TIMEOUT_MS); } /** Restart connectivity for all requested subsystems. */ public void restart() { if (!mConnectivitySubsystemsRecoveryManager.isRecoveryAvailable()) { if (!mRecoveryWorker.isRecoveryAvailable()) { Log.e(TAG, "The connectivity subsystem is not available to restart."); return; } showResettingAndSendTimeoutChecks(); mIsWifiReady = !mWifiManager.isWifiEnabled(); mRecoveryWorker.triggerRestart(); } /** Check if the connectivity subsystem is under recovering. */ public void checkRecovering() { if (!mRecoveryWorker.isRecovering()) return; mIsWifiReady = false; showResettingAndSendTimeoutChecks(); } /** * This is a singleton class for ConnectivitySubsystemsRecoveryManager worker. */ @VisibleForTesting public static class RecoveryWorker implements ConnectivitySubsystemsRecoveryManager.RecoveryStatusCallback { private static final String TAG = "RecoveryWorker"; private static RecoveryWorker sInstance; private static WeakReference<InternetResetHelper> sCallback; private static ConnectivitySubsystemsRecoveryManager sRecoveryManager; private static boolean sIsRecovering; /** * Create a singleton class for ConnectivitySubsystemsRecoveryManager. * * @param context The context to use for the content resolver. * @param callback The callback of {@link InternetResetHelper} object. * @return an instance of {@link RecoveryWorker} object. */ public static RecoveryWorker getInstance(Context context, InternetResetHelper callback) { sCallback = new WeakReference<>(callback); if (sInstance != null) return sInstance; sInstance = new RecoveryWorker(); Context appContext = context.getApplicationContext(); sRecoveryManager = new ConnectivitySubsystemsRecoveryManager(appContext, appContext.getMainThreadHandler()); return sInstance; } /** Returns true, If the subsystem service is recovering. */ public boolean isRecovering() { return sIsRecovering; } /** Clear the recovering flag. */ public void clearRecovering() { sIsRecovering = false; } /** Returns true, If the subsystem service is recovery available. */ public boolean isRecoveryAvailable() { return sRecoveryManager.isRecoveryAvailable(); } /** Trigger connectivity recovery for all requested technologies. */ public boolean triggerRestart() { if (!isRecoveryAvailable()) { Log.e(TAG, "The connectivity subsystem is not available to restart."); return false; } sIsRecovering = true; sRecoveryManager.triggerSubsystemRestart(null /* reason */, sInstance); Log.d(TAG, "The connectivity subsystem is restarting for recovery."); suspendPreferences(); mIsRecoveryReady = false; mIsWifiReady = !mWifiManager.isWifiEnabled(); mHandlerInjector.postDelayed(mTimeoutRunnable, RESTART_TIMEOUT_MS); mConnectivitySubsystemsRecoveryManager.triggerSubsystemRestart(null /* reason */, this); return true; } @Override public void onSubsystemRestartOperationBegin() { Log.d(TAG, "The connectivity subsystem is starting for recovery."); sIsRecovering = true; } @Override public void onSubsystemRestartOperationEnd() { Log.d(TAG, "The connectivity subsystem is done for recovery."); sIsRecovering = false; InternetResetHelper callback = sCallback.get(); if (callback == null) return; callback.resumePreferences(); } } } src/com/android/settings/network/NetworkProviderSettings.java +13 −10 Original line number Diff line number Diff line Loading @@ -334,6 +334,8 @@ public class NetworkProviderSettings extends RestrictedSettingsFragment addConnectedEthernetNetworkController(); addWifiSwitchPreferenceController(); mWifiStatusMessagePreference = findPreference(PREF_KEY_WIFI_STATUS_MESSAGE); checkConnectivityRecovering(); } private void updateAirplaneModeMsgPreference(boolean visible) { Loading Loading @@ -379,6 +381,17 @@ public class NetworkProviderSettings extends RestrictedSettingsFragment mWifiSwitchPreferenceController.displayPreference(getPreferenceScreen()); } private void checkConnectivityRecovering() { mInternetResetHelper = new InternetResetHelper(getContext(), getLifecycle(), mNetworkMobileProviderController, findPreference(WifiSwitchPreferenceController.KEY), mConnectedWifiEntryPreferenceCategory, mFirstWifiEntryPreferenceCategory, mWifiEntryPreferenceCategory, mResetInternetPreference); mInternetResetHelper.checkRecovering(); } @Override public void onActivityCreated(Bundle savedInstanceState) { super.onActivityCreated(savedInstanceState); Loading Loading @@ -1447,16 +1460,6 @@ public class NetworkProviderSettings extends RestrictedSettingsFragment } private void fixConnectivity() { if (mInternetResetHelper == null) { mInternetResetHelper = new InternetResetHelper(getContext(), getLifecycle()); mInternetResetHelper.setResettingPreference(mResetInternetPreference); mInternetResetHelper.setMobileNetworkController(mNetworkMobileProviderController); mInternetResetHelper.setWifiTogglePreference( findPreference(WifiSwitchPreferenceController.KEY)); mInternetResetHelper.addWifiNetworkPreference(mConnectedWifiEntryPreferenceCategory); mInternetResetHelper.addWifiNetworkPreference(mFirstWifiEntryPreferenceCategory); mInternetResetHelper.addWifiNetworkPreference(mWifiEntryPreferenceCategory); } mInternetResetHelper.restart(); } Loading tests/unit/src/com/android/settings/network/InternetResetHelperTest.java +55 −79 Original line number Diff line number Diff line Loading @@ -16,6 +16,8 @@ package com.android.settings.network; import static com.android.settings.network.InternetResetHelper.RESTART_TIMEOUT_MS; import static com.google.common.truth.Truth.assertThat; import static org.mockito.ArgumentMatchers.any; Loading @@ -29,8 +31,6 @@ import android.content.BroadcastReceiver; import android.content.Context; import android.content.IntentFilter; import android.net.wifi.WifiManager; import android.os.Handler; import android.os.HandlerThread; import android.os.Looper; import androidx.lifecycle.Lifecycle; Loading @@ -39,7 +39,6 @@ import androidx.preference.PreferenceCategory; import androidx.test.core.app.ApplicationProvider; import androidx.test.ext.junit.runners.AndroidJUnit4; import com.android.settingslib.connectivity.ConnectivitySubsystemsRecoveryManager; import com.android.settingslib.utils.HandlerInjector; import org.junit.Before; Loading @@ -47,6 +46,7 @@ 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; Loading @@ -55,66 +55,46 @@ public class InternetResetHelperTest { @Rule public final MockitoRule mMockitoRule = MockitoJUnit.rule(); @Spy private final Context mContext = ApplicationProvider.getApplicationContext(); @Mock private WifiManager mWifiManager; @Mock public HandlerThread mWorkerThread; InternetResetHelper.RecoveryWorker mRecoveryWorker; @Mock public ConnectivitySubsystemsRecoveryManager mConnectivitySubsystemsRecoveryManager; HandlerInjector mHandlerInjector; @Mock public NetworkMobileProviderController mMobileNetworkController; private Context mContext; private InternetResetHelper mInternetResetHelper; private Preference mResettingPreference; private Preference mWifiTogglePreferences; private PreferenceCategory mConnectedWifiEntryPreferences; private PreferenceCategory mFirstWifiEntryPreference; private PreferenceCategory mWifiEntryPreferences; private FakeHandlerInjector mFakeHandlerInjector; private static class FakeHandlerInjector extends HandlerInjector { private Runnable mRunnable; FakeHandlerInjector(Handler handler) { super(handler); } @Override public void postDelayed(Runnable runnable, long delayMillis) { mRunnable = runnable; } public Runnable getRunnable() { return mRunnable; } } @Before public void setUp() { mContext = spy(ApplicationProvider.getApplicationContext()); when(mContext.getSystemService(WifiManager.class)).thenReturn(mWifiManager); when(mRecoveryWorker.isRecovering()).thenReturn(false); if (Looper.myLooper() == null) { Looper.prepare(); } mResettingPreference = new Preference(mContext); mResettingPreference = spy(new Preference(mContext)); mWifiTogglePreferences = new Preference(mContext); mConnectedWifiEntryPreferences = spy(new PreferenceCategory(mContext)); mFirstWifiEntryPreference = spy(new PreferenceCategory(mContext)); mWifiEntryPreferences = spy(new PreferenceCategory(mContext)); final Lifecycle lifecycle = mock(Lifecycle.class); mInternetResetHelper = new InternetResetHelper(mContext, lifecycle); mInternetResetHelper.mWorkerThread = mWorkerThread; mFakeHandlerInjector = new FakeHandlerInjector(mContext.getMainThreadHandler()); mInternetResetHelper.mHandlerInjector = mFakeHandlerInjector; mInternetResetHelper.mConnectivitySubsystemsRecoveryManager = mConnectivitySubsystemsRecoveryManager; mInternetResetHelper.setResettingPreference(mResettingPreference); mInternetResetHelper.setMobileNetworkController(mMobileNetworkController); mInternetResetHelper.setWifiTogglePreference(mWifiTogglePreferences); mInternetResetHelper.addWifiNetworkPreference(mConnectedWifiEntryPreferences); mInternetResetHelper.addWifiNetworkPreference(mWifiEntryPreferences); mInternetResetHelper = new InternetResetHelper(mContext, mock(Lifecycle.class), mMobileNetworkController, mWifiTogglePreferences, mConnectedWifiEntryPreferences, mFirstWifiEntryPreference, mWifiEntryPreferences, mResettingPreference); mInternetResetHelper.mHandlerInjector = mHandlerInjector; mInternetResetHelper.mRecoveryWorker = mRecoveryWorker; } @Test Loading @@ -135,30 +115,10 @@ public class InternetResetHelperTest { } @Test public void onDestroy_quitWorkerThread() { public void onDestroy_removeCallbacks() { mInternetResetHelper.onDestroy(); verify(mWorkerThread).quit(); } @Test public void onSubsystemRestartOperationEnd_recoveryIsNotReady_postResumeRunnable() { mInternetResetHelper.mIsRecoveryReady = false; mInternetResetHelper.onSubsystemRestartOperationEnd(); assertThat(mInternetResetHelper.mIsRecoveryReady).isTrue(); assertThat(mFakeHandlerInjector.getRunnable()) .isEqualTo(mInternetResetHelper.mResumeRunnable); } @Test public void onSubsystemRestartOperationEnd_recoveryIsReady_doNothing() { mInternetResetHelper.mIsRecoveryReady = true; mInternetResetHelper.onSubsystemRestartOperationEnd(); assertThat(mFakeHandlerInjector.getRunnable()).isNull(); verify(mHandlerInjector).removeCallbacks(any()); } @Test Loading @@ -169,19 +129,16 @@ public class InternetResetHelperTest { mInternetResetHelper.updateWifiStateChange(); assertThat(mInternetResetHelper.mIsWifiReady).isFalse(); assertThat(mFakeHandlerInjector.getRunnable()).isNull(); } @Test public void updateWifiStateChange_wifiIsNotReadyAndWifiEnabled_postResumeRunnable() { public void updateWifiStateChange_wifiIsNotReadyAndWifiEnabled_updateWifiIsReady() { mInternetResetHelper.mIsWifiReady = false; when(mWifiManager.isWifiEnabled()).thenReturn(true); mInternetResetHelper.updateWifiStateChange(); assertThat(mInternetResetHelper.mIsWifiReady).isTrue(); assertThat(mFakeHandlerInjector.getRunnable()) .isEqualTo(mInternetResetHelper.mResumeRunnable); } @Test Loading @@ -191,7 +148,6 @@ public class InternetResetHelperTest { mInternetResetHelper.updateWifiStateChange(); assertThat(mInternetResetHelper.mIsWifiReady).isTrue(); assertThat(mFakeHandlerInjector.getRunnable()).isNull(); } @Test Loading @@ -203,16 +159,15 @@ public class InternetResetHelperTest { // Hide subsystem preferences verify(mMobileNetworkController).hidePreference(true /* hide */, true /* immediately*/); assertThat(mWifiTogglePreferences.isVisible()).isFalse(); verify(mConnectedWifiEntryPreferences).removeAll(); assertThat(mConnectedWifiEntryPreferences.isVisible()).isFalse(); verify(mWifiEntryPreferences).removeAll(); assertThat(mFirstWifiEntryPreference.isVisible()).isFalse(); assertThat(mWifiEntryPreferences.isVisible()).isFalse(); } @Test public void resumePreferences_onlyRecoveryReady_shouldShowSubSysHideResetting() { mInternetResetHelper.suspendPreferences(); mInternetResetHelper.mIsRecoveryReady = true; when(mRecoveryWorker.isRecovering()).thenReturn(false); mInternetResetHelper.mIsWifiReady = false; mInternetResetHelper.resumePreferences(); Loading @@ -224,13 +179,14 @@ public class InternetResetHelperTest { // Hide Wi-Fi preferences assertThat(mWifiTogglePreferences.isVisible()).isFalse(); assertThat(mConnectedWifiEntryPreferences.isVisible()).isFalse(); assertThat(mFirstWifiEntryPreference.isVisible()).isFalse(); assertThat(mWifiEntryPreferences.isVisible()).isFalse(); } @Test public void resumePreferences_onlyWifiReady_shouldShowSubSysHideResetting() { mInternetResetHelper.suspendPreferences(); mInternetResetHelper.mIsRecoveryReady = false; when(mRecoveryWorker.isRecovering()).thenReturn(true); mInternetResetHelper.mIsWifiReady = true; mInternetResetHelper.resumePreferences(); Loading @@ -240,6 +196,7 @@ public class InternetResetHelperTest { // Show Wi-Fi preferences assertThat(mWifiTogglePreferences.isVisible()).isTrue(); assertThat(mConnectedWifiEntryPreferences.isVisible()).isTrue(); assertThat(mFirstWifiEntryPreference.isVisible()).isTrue(); assertThat(mWifiEntryPreferences.isVisible()).isTrue(); // Hide Mobile Network controller verify(mMobileNetworkController, never()) Loading @@ -249,14 +206,16 @@ public class InternetResetHelperTest { @Test public void resumePreferences_allReady_shouldShowSubSysHideResetting() { mInternetResetHelper.suspendPreferences(); mInternetResetHelper.mIsRecoveryReady = true; when(mRecoveryWorker.isRecovering()).thenReturn(false); mInternetResetHelper.mIsWifiReady = true; mInternetResetHelper.resumePreferences(); // Show subsystem preferences verify(mMobileNetworkController).hidePreference(false, true); assertThat(mWifiTogglePreferences.isVisible()).isTrue(); assertThat(mConnectedWifiEntryPreferences.isVisible()).isTrue(); assertThat(mFirstWifiEntryPreference.isVisible()).isTrue(); assertThat(mWifiEntryPreferences.isVisible()).isTrue(); // Hide resetting preference assertThat(mResettingPreference.isVisible()).isFalse(); Loading @@ -264,22 +223,39 @@ public class InternetResetHelperTest { @Test public void restart_recoveryNotAvailable_shouldDoTriggerSubsystemRestart() { when(mConnectivitySubsystemsRecoveryManager.isRecoveryAvailable()).thenReturn(false); when(mRecoveryWorker.isRecoveryAvailable()).thenReturn(false); mInternetResetHelper.restart(); verify(mConnectivitySubsystemsRecoveryManager, never()) .triggerSubsystemRestart(any(), any()); verify(mRecoveryWorker, never()).triggerRestart(); } @Test public void restart_recoveryAvailable_triggerSubsystemRestart() { when(mConnectivitySubsystemsRecoveryManager.isRecoveryAvailable()).thenReturn(true); when(mRecoveryWorker.isRecoveryAvailable()).thenReturn(true); mInternetResetHelper.restart(); assertThat(mFakeHandlerInjector.getRunnable()) .isEqualTo(mInternetResetHelper.mTimeoutRunnable); verify(mConnectivitySubsystemsRecoveryManager).triggerSubsystemRestart(any(), any()); verify(mHandlerInjector) .postDelayed(mInternetResetHelper.mTimeoutRunnable, RESTART_TIMEOUT_MS); verify(mRecoveryWorker).triggerRestart(); } @Test public void checkRecovering_isRecovering_showResetting() { when(mRecoveryWorker.isRecovering()).thenReturn(true); mInternetResetHelper.checkRecovering(); verify(mResettingPreference).setVisible(true); } @Test public void checkRecovering_isNotRecovering_doNotShowResetting() { when(mRecoveryWorker.isRecovering()).thenReturn(false); mInternetResetHelper.checkRecovering(); verify(mResettingPreference, never()).setVisible(true); } } Loading
src/com/android/settings/network/InternetResetHelper.java +115 −97 Original line number Diff line number Diff line Loading @@ -21,14 +21,9 @@ 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.text.TextUtils; import android.util.Log; import androidx.annotation.UiThread; import androidx.annotation.VisibleForTesting; import androidx.annotation.WorkerThread; import androidx.lifecycle.Lifecycle; import androidx.lifecycle.LifecycleObserver; import androidx.lifecycle.OnLifecycleEvent; Loading @@ -38,14 +33,14 @@ import androidx.preference.PreferenceCategory; import com.android.settingslib.connectivity.ConnectivitySubsystemsRecoveryManager; import com.android.settingslib.utils.HandlerInjector; import java.lang.ref.WeakReference; import java.util.ArrayList; import java.util.List; /** * Helper class to restart connectivity for all requested subsystems. */ public class InternetResetHelper implements LifecycleObserver, ConnectivitySubsystemsRecoveryManager.RecoveryStatusCallback { public class InternetResetHelper implements LifecycleObserver { protected static final String TAG = "InternetResetHelper"; public static final long RESTART_TIMEOUT_MS = 15_000; // 15 seconds Loading @@ -61,41 +56,40 @@ public class InternetResetHelper implements LifecycleObserver, protected final IntentFilter mWifiStateFilter; protected final BroadcastReceiver mWifiStateReceiver = new BroadcastReceiver() { @Override @WorkerThread public void onReceive(Context context, Intent intent) { if (intent != null && TextUtils.equals(intent.getAction(), WifiManager.NETWORK_STATE_CHANGED_ACTION)) { updateWifiStateChange(); } } }; protected ConnectivitySubsystemsRecoveryManager mConnectivitySubsystemsRecoveryManager; protected HandlerThread mWorkerThread; protected boolean mIsRecoveryReady; protected boolean mIsWifiReady; protected RecoveryWorker mRecoveryWorker; protected boolean mIsWifiReady = true; protected HandlerInjector mHandlerInjector; protected final Runnable mResumeRunnable = () -> { resumePreferences(); }; protected final Runnable mTimeoutRunnable = () -> { mIsRecoveryReady = true; Log.w(TAG, "Resume preferences due to connectivity subsystems recovery timed out."); mRecoveryWorker.clearRecovering(); mIsWifiReady = true; resumePreferences(); }; public InternetResetHelper(Context context, Lifecycle lifecycle) { public InternetResetHelper(Context context, Lifecycle lifecycle, NetworkMobileProviderController mobileNetworkController, Preference wifiTogglePreferences, PreferenceCategory connectedWifiEntryPreferenceCategory, PreferenceCategory firstWifiEntryPreferenceCategory, PreferenceCategory wifiEntryPreferenceCategory, Preference resettingPreference) { mContext = context; mMobileNetworkController = mobileNetworkController; mWifiTogglePreferences = wifiTogglePreferences; mWifiNetworkPreferences.add(connectedWifiEntryPreferenceCategory); mWifiNetworkPreferences.add(firstWifiEntryPreferenceCategory); mWifiNetworkPreferences.add(wifiEntryPreferenceCategory); mResettingPreference = resettingPreference; mHandlerInjector = new HandlerInjector(context.getMainThreadHandler()); mWifiManager = mContext.getSystemService(WifiManager.class); mWifiStateFilter = new IntentFilter(WifiManager.NETWORK_STATE_CHANGED_ACTION); mWorkerThread = new HandlerThread(TAG + "{" + Integer.toHexString(System.identityHashCode(this)) + "}", Process.THREAD_PRIORITY_BACKGROUND); mWorkerThread.start(); mConnectivitySubsystemsRecoveryManager = new ConnectivitySubsystemsRecoveryManager( mContext, mWorkerThread.getThreadHandler()); mRecoveryWorker = RecoveryWorker.getInstance(mContext, this); if (lifecycle != null) { lifecycle.addObserver(this); Loading @@ -118,72 +112,18 @@ public class InternetResetHelper implements LifecycleObserver, /** @OnLifecycleEvent(Lifecycle.Event.ON_DESTROY) */ @OnLifecycleEvent(Lifecycle.Event.ON_DESTROY) public void onDestroy() { mHandlerInjector.removeCallbacks(mResumeRunnable); mHandlerInjector.removeCallbacks(mTimeoutRunnable); mWorkerThread.quit(); } @Override @WorkerThread public void onSubsystemRestartOperationBegin() { Log.d(TAG, "The connectivity subsystem is starting for recovery."); } @Override @WorkerThread public void onSubsystemRestartOperationEnd() { Log.d(TAG, "The connectivity subsystem is done for recovery."); if (!mIsRecoveryReady) { mIsRecoveryReady = true; mHandlerInjector.postDelayed(mResumeRunnable, 0 /* delayMillis */); } } @VisibleForTesting @WorkerThread protected void updateWifiStateChange() { if (!mIsWifiReady && mWifiManager.isWifiEnabled()) { Log.d(TAG, "The Wi-Fi subsystem is done for recovery."); mIsWifiReady = true; mHandlerInjector.postDelayed(mResumeRunnable, 0 /* delayMillis */); } } /** * Sets the resetting preference. */ @UiThread public void setResettingPreference(Preference preference) { mResettingPreference = preference; } /** * Sets the mobile network controller. */ @UiThread public void setMobileNetworkController(NetworkMobileProviderController controller) { mMobileNetworkController = controller; } /** * Sets the Wi-Fi toggle preference. */ @UiThread public void setWifiTogglePreference(Preference preference) { mWifiTogglePreferences = preference; } /** * Adds the Wi-Fi network preference. */ @UiThread public void addWifiNetworkPreference(PreferenceCategory preference) { if (preference != null) { mWifiNetworkPreferences.add(preference); resumePreferences(); } } @UiThread protected void suspendPreferences() { Log.d(TAG, "Suspend the subsystem preferences"); if (mMobileNetworkController != null) { Loading @@ -201,9 +141,9 @@ public class InternetResetHelper implements LifecycleObserver, } } @UiThread protected void resumePreferences() { if (mIsRecoveryReady && mMobileNetworkController != null) { boolean isRecoveryReady = !mRecoveryWorker.isRecovering(); if (isRecoveryReady && mMobileNetworkController != null) { Log.d(TAG, "Resume the Mobile Network controller"); mMobileNetworkController.hidePreference(false /* hide */, true /* immediately */); } Loading @@ -214,7 +154,7 @@ public class InternetResetHelper implements LifecycleObserver, pref.setVisible(true); } } if (mIsRecoveryReady && mIsWifiReady) { if (isRecoveryReady && mIsWifiReady) { mHandlerInjector.removeCallbacks(mTimeoutRunnable); if (mResettingPreference != null) { Log.d(TAG, "Resume the Resetting preference"); Loading @@ -223,21 +163,99 @@ public class InternetResetHelper implements LifecycleObserver, } } /** * Restart connectivity for all requested subsystems. */ @UiThread protected void showResettingAndSendTimeoutChecks() { suspendPreferences(); mHandlerInjector.postDelayed(mTimeoutRunnable, RESTART_TIMEOUT_MS); } /** Restart connectivity for all requested subsystems. */ public void restart() { if (!mConnectivitySubsystemsRecoveryManager.isRecoveryAvailable()) { if (!mRecoveryWorker.isRecoveryAvailable()) { Log.e(TAG, "The connectivity subsystem is not available to restart."); return; } showResettingAndSendTimeoutChecks(); mIsWifiReady = !mWifiManager.isWifiEnabled(); mRecoveryWorker.triggerRestart(); } /** Check if the connectivity subsystem is under recovering. */ public void checkRecovering() { if (!mRecoveryWorker.isRecovering()) return; mIsWifiReady = false; showResettingAndSendTimeoutChecks(); } /** * This is a singleton class for ConnectivitySubsystemsRecoveryManager worker. */ @VisibleForTesting public static class RecoveryWorker implements ConnectivitySubsystemsRecoveryManager.RecoveryStatusCallback { private static final String TAG = "RecoveryWorker"; private static RecoveryWorker sInstance; private static WeakReference<InternetResetHelper> sCallback; private static ConnectivitySubsystemsRecoveryManager sRecoveryManager; private static boolean sIsRecovering; /** * Create a singleton class for ConnectivitySubsystemsRecoveryManager. * * @param context The context to use for the content resolver. * @param callback The callback of {@link InternetResetHelper} object. * @return an instance of {@link RecoveryWorker} object. */ public static RecoveryWorker getInstance(Context context, InternetResetHelper callback) { sCallback = new WeakReference<>(callback); if (sInstance != null) return sInstance; sInstance = new RecoveryWorker(); Context appContext = context.getApplicationContext(); sRecoveryManager = new ConnectivitySubsystemsRecoveryManager(appContext, appContext.getMainThreadHandler()); return sInstance; } /** Returns true, If the subsystem service is recovering. */ public boolean isRecovering() { return sIsRecovering; } /** Clear the recovering flag. */ public void clearRecovering() { sIsRecovering = false; } /** Returns true, If the subsystem service is recovery available. */ public boolean isRecoveryAvailable() { return sRecoveryManager.isRecoveryAvailable(); } /** Trigger connectivity recovery for all requested technologies. */ public boolean triggerRestart() { if (!isRecoveryAvailable()) { Log.e(TAG, "The connectivity subsystem is not available to restart."); return false; } sIsRecovering = true; sRecoveryManager.triggerSubsystemRestart(null /* reason */, sInstance); Log.d(TAG, "The connectivity subsystem is restarting for recovery."); suspendPreferences(); mIsRecoveryReady = false; mIsWifiReady = !mWifiManager.isWifiEnabled(); mHandlerInjector.postDelayed(mTimeoutRunnable, RESTART_TIMEOUT_MS); mConnectivitySubsystemsRecoveryManager.triggerSubsystemRestart(null /* reason */, this); return true; } @Override public void onSubsystemRestartOperationBegin() { Log.d(TAG, "The connectivity subsystem is starting for recovery."); sIsRecovering = true; } @Override public void onSubsystemRestartOperationEnd() { Log.d(TAG, "The connectivity subsystem is done for recovery."); sIsRecovering = false; InternetResetHelper callback = sCallback.get(); if (callback == null) return; callback.resumePreferences(); } } }
src/com/android/settings/network/NetworkProviderSettings.java +13 −10 Original line number Diff line number Diff line Loading @@ -334,6 +334,8 @@ public class NetworkProviderSettings extends RestrictedSettingsFragment addConnectedEthernetNetworkController(); addWifiSwitchPreferenceController(); mWifiStatusMessagePreference = findPreference(PREF_KEY_WIFI_STATUS_MESSAGE); checkConnectivityRecovering(); } private void updateAirplaneModeMsgPreference(boolean visible) { Loading Loading @@ -379,6 +381,17 @@ public class NetworkProviderSettings extends RestrictedSettingsFragment mWifiSwitchPreferenceController.displayPreference(getPreferenceScreen()); } private void checkConnectivityRecovering() { mInternetResetHelper = new InternetResetHelper(getContext(), getLifecycle(), mNetworkMobileProviderController, findPreference(WifiSwitchPreferenceController.KEY), mConnectedWifiEntryPreferenceCategory, mFirstWifiEntryPreferenceCategory, mWifiEntryPreferenceCategory, mResetInternetPreference); mInternetResetHelper.checkRecovering(); } @Override public void onActivityCreated(Bundle savedInstanceState) { super.onActivityCreated(savedInstanceState); Loading Loading @@ -1447,16 +1460,6 @@ public class NetworkProviderSettings extends RestrictedSettingsFragment } private void fixConnectivity() { if (mInternetResetHelper == null) { mInternetResetHelper = new InternetResetHelper(getContext(), getLifecycle()); mInternetResetHelper.setResettingPreference(mResetInternetPreference); mInternetResetHelper.setMobileNetworkController(mNetworkMobileProviderController); mInternetResetHelper.setWifiTogglePreference( findPreference(WifiSwitchPreferenceController.KEY)); mInternetResetHelper.addWifiNetworkPreference(mConnectedWifiEntryPreferenceCategory); mInternetResetHelper.addWifiNetworkPreference(mFirstWifiEntryPreferenceCategory); mInternetResetHelper.addWifiNetworkPreference(mWifiEntryPreferenceCategory); } mInternetResetHelper.restart(); } Loading
tests/unit/src/com/android/settings/network/InternetResetHelperTest.java +55 −79 Original line number Diff line number Diff line Loading @@ -16,6 +16,8 @@ package com.android.settings.network; import static com.android.settings.network.InternetResetHelper.RESTART_TIMEOUT_MS; import static com.google.common.truth.Truth.assertThat; import static org.mockito.ArgumentMatchers.any; Loading @@ -29,8 +31,6 @@ import android.content.BroadcastReceiver; import android.content.Context; import android.content.IntentFilter; import android.net.wifi.WifiManager; import android.os.Handler; import android.os.HandlerThread; import android.os.Looper; import androidx.lifecycle.Lifecycle; Loading @@ -39,7 +39,6 @@ import androidx.preference.PreferenceCategory; import androidx.test.core.app.ApplicationProvider; import androidx.test.ext.junit.runners.AndroidJUnit4; import com.android.settingslib.connectivity.ConnectivitySubsystemsRecoveryManager; import com.android.settingslib.utils.HandlerInjector; import org.junit.Before; Loading @@ -47,6 +46,7 @@ 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; Loading @@ -55,66 +55,46 @@ public class InternetResetHelperTest { @Rule public final MockitoRule mMockitoRule = MockitoJUnit.rule(); @Spy private final Context mContext = ApplicationProvider.getApplicationContext(); @Mock private WifiManager mWifiManager; @Mock public HandlerThread mWorkerThread; InternetResetHelper.RecoveryWorker mRecoveryWorker; @Mock public ConnectivitySubsystemsRecoveryManager mConnectivitySubsystemsRecoveryManager; HandlerInjector mHandlerInjector; @Mock public NetworkMobileProviderController mMobileNetworkController; private Context mContext; private InternetResetHelper mInternetResetHelper; private Preference mResettingPreference; private Preference mWifiTogglePreferences; private PreferenceCategory mConnectedWifiEntryPreferences; private PreferenceCategory mFirstWifiEntryPreference; private PreferenceCategory mWifiEntryPreferences; private FakeHandlerInjector mFakeHandlerInjector; private static class FakeHandlerInjector extends HandlerInjector { private Runnable mRunnable; FakeHandlerInjector(Handler handler) { super(handler); } @Override public void postDelayed(Runnable runnable, long delayMillis) { mRunnable = runnable; } public Runnable getRunnable() { return mRunnable; } } @Before public void setUp() { mContext = spy(ApplicationProvider.getApplicationContext()); when(mContext.getSystemService(WifiManager.class)).thenReturn(mWifiManager); when(mRecoveryWorker.isRecovering()).thenReturn(false); if (Looper.myLooper() == null) { Looper.prepare(); } mResettingPreference = new Preference(mContext); mResettingPreference = spy(new Preference(mContext)); mWifiTogglePreferences = new Preference(mContext); mConnectedWifiEntryPreferences = spy(new PreferenceCategory(mContext)); mFirstWifiEntryPreference = spy(new PreferenceCategory(mContext)); mWifiEntryPreferences = spy(new PreferenceCategory(mContext)); final Lifecycle lifecycle = mock(Lifecycle.class); mInternetResetHelper = new InternetResetHelper(mContext, lifecycle); mInternetResetHelper.mWorkerThread = mWorkerThread; mFakeHandlerInjector = new FakeHandlerInjector(mContext.getMainThreadHandler()); mInternetResetHelper.mHandlerInjector = mFakeHandlerInjector; mInternetResetHelper.mConnectivitySubsystemsRecoveryManager = mConnectivitySubsystemsRecoveryManager; mInternetResetHelper.setResettingPreference(mResettingPreference); mInternetResetHelper.setMobileNetworkController(mMobileNetworkController); mInternetResetHelper.setWifiTogglePreference(mWifiTogglePreferences); mInternetResetHelper.addWifiNetworkPreference(mConnectedWifiEntryPreferences); mInternetResetHelper.addWifiNetworkPreference(mWifiEntryPreferences); mInternetResetHelper = new InternetResetHelper(mContext, mock(Lifecycle.class), mMobileNetworkController, mWifiTogglePreferences, mConnectedWifiEntryPreferences, mFirstWifiEntryPreference, mWifiEntryPreferences, mResettingPreference); mInternetResetHelper.mHandlerInjector = mHandlerInjector; mInternetResetHelper.mRecoveryWorker = mRecoveryWorker; } @Test Loading @@ -135,30 +115,10 @@ public class InternetResetHelperTest { } @Test public void onDestroy_quitWorkerThread() { public void onDestroy_removeCallbacks() { mInternetResetHelper.onDestroy(); verify(mWorkerThread).quit(); } @Test public void onSubsystemRestartOperationEnd_recoveryIsNotReady_postResumeRunnable() { mInternetResetHelper.mIsRecoveryReady = false; mInternetResetHelper.onSubsystemRestartOperationEnd(); assertThat(mInternetResetHelper.mIsRecoveryReady).isTrue(); assertThat(mFakeHandlerInjector.getRunnable()) .isEqualTo(mInternetResetHelper.mResumeRunnable); } @Test public void onSubsystemRestartOperationEnd_recoveryIsReady_doNothing() { mInternetResetHelper.mIsRecoveryReady = true; mInternetResetHelper.onSubsystemRestartOperationEnd(); assertThat(mFakeHandlerInjector.getRunnable()).isNull(); verify(mHandlerInjector).removeCallbacks(any()); } @Test Loading @@ -169,19 +129,16 @@ public class InternetResetHelperTest { mInternetResetHelper.updateWifiStateChange(); assertThat(mInternetResetHelper.mIsWifiReady).isFalse(); assertThat(mFakeHandlerInjector.getRunnable()).isNull(); } @Test public void updateWifiStateChange_wifiIsNotReadyAndWifiEnabled_postResumeRunnable() { public void updateWifiStateChange_wifiIsNotReadyAndWifiEnabled_updateWifiIsReady() { mInternetResetHelper.mIsWifiReady = false; when(mWifiManager.isWifiEnabled()).thenReturn(true); mInternetResetHelper.updateWifiStateChange(); assertThat(mInternetResetHelper.mIsWifiReady).isTrue(); assertThat(mFakeHandlerInjector.getRunnable()) .isEqualTo(mInternetResetHelper.mResumeRunnable); } @Test Loading @@ -191,7 +148,6 @@ public class InternetResetHelperTest { mInternetResetHelper.updateWifiStateChange(); assertThat(mInternetResetHelper.mIsWifiReady).isTrue(); assertThat(mFakeHandlerInjector.getRunnable()).isNull(); } @Test Loading @@ -203,16 +159,15 @@ public class InternetResetHelperTest { // Hide subsystem preferences verify(mMobileNetworkController).hidePreference(true /* hide */, true /* immediately*/); assertThat(mWifiTogglePreferences.isVisible()).isFalse(); verify(mConnectedWifiEntryPreferences).removeAll(); assertThat(mConnectedWifiEntryPreferences.isVisible()).isFalse(); verify(mWifiEntryPreferences).removeAll(); assertThat(mFirstWifiEntryPreference.isVisible()).isFalse(); assertThat(mWifiEntryPreferences.isVisible()).isFalse(); } @Test public void resumePreferences_onlyRecoveryReady_shouldShowSubSysHideResetting() { mInternetResetHelper.suspendPreferences(); mInternetResetHelper.mIsRecoveryReady = true; when(mRecoveryWorker.isRecovering()).thenReturn(false); mInternetResetHelper.mIsWifiReady = false; mInternetResetHelper.resumePreferences(); Loading @@ -224,13 +179,14 @@ public class InternetResetHelperTest { // Hide Wi-Fi preferences assertThat(mWifiTogglePreferences.isVisible()).isFalse(); assertThat(mConnectedWifiEntryPreferences.isVisible()).isFalse(); assertThat(mFirstWifiEntryPreference.isVisible()).isFalse(); assertThat(mWifiEntryPreferences.isVisible()).isFalse(); } @Test public void resumePreferences_onlyWifiReady_shouldShowSubSysHideResetting() { mInternetResetHelper.suspendPreferences(); mInternetResetHelper.mIsRecoveryReady = false; when(mRecoveryWorker.isRecovering()).thenReturn(true); mInternetResetHelper.mIsWifiReady = true; mInternetResetHelper.resumePreferences(); Loading @@ -240,6 +196,7 @@ public class InternetResetHelperTest { // Show Wi-Fi preferences assertThat(mWifiTogglePreferences.isVisible()).isTrue(); assertThat(mConnectedWifiEntryPreferences.isVisible()).isTrue(); assertThat(mFirstWifiEntryPreference.isVisible()).isTrue(); assertThat(mWifiEntryPreferences.isVisible()).isTrue(); // Hide Mobile Network controller verify(mMobileNetworkController, never()) Loading @@ -249,14 +206,16 @@ public class InternetResetHelperTest { @Test public void resumePreferences_allReady_shouldShowSubSysHideResetting() { mInternetResetHelper.suspendPreferences(); mInternetResetHelper.mIsRecoveryReady = true; when(mRecoveryWorker.isRecovering()).thenReturn(false); mInternetResetHelper.mIsWifiReady = true; mInternetResetHelper.resumePreferences(); // Show subsystem preferences verify(mMobileNetworkController).hidePreference(false, true); assertThat(mWifiTogglePreferences.isVisible()).isTrue(); assertThat(mConnectedWifiEntryPreferences.isVisible()).isTrue(); assertThat(mFirstWifiEntryPreference.isVisible()).isTrue(); assertThat(mWifiEntryPreferences.isVisible()).isTrue(); // Hide resetting preference assertThat(mResettingPreference.isVisible()).isFalse(); Loading @@ -264,22 +223,39 @@ public class InternetResetHelperTest { @Test public void restart_recoveryNotAvailable_shouldDoTriggerSubsystemRestart() { when(mConnectivitySubsystemsRecoveryManager.isRecoveryAvailable()).thenReturn(false); when(mRecoveryWorker.isRecoveryAvailable()).thenReturn(false); mInternetResetHelper.restart(); verify(mConnectivitySubsystemsRecoveryManager, never()) .triggerSubsystemRestart(any(), any()); verify(mRecoveryWorker, never()).triggerRestart(); } @Test public void restart_recoveryAvailable_triggerSubsystemRestart() { when(mConnectivitySubsystemsRecoveryManager.isRecoveryAvailable()).thenReturn(true); when(mRecoveryWorker.isRecoveryAvailable()).thenReturn(true); mInternetResetHelper.restart(); assertThat(mFakeHandlerInjector.getRunnable()) .isEqualTo(mInternetResetHelper.mTimeoutRunnable); verify(mConnectivitySubsystemsRecoveryManager).triggerSubsystemRestart(any(), any()); verify(mHandlerInjector) .postDelayed(mInternetResetHelper.mTimeoutRunnable, RESTART_TIMEOUT_MS); verify(mRecoveryWorker).triggerRestart(); } @Test public void checkRecovering_isRecovering_showResetting() { when(mRecoveryWorker.isRecovering()).thenReturn(true); mInternetResetHelper.checkRecovering(); verify(mResettingPreference).setVisible(true); } @Test public void checkRecovering_isNotRecovering_doNotShowResetting() { when(mRecoveryWorker.isRecovering()).thenReturn(false); mInternetResetHelper.checkRecovering(); verify(mResettingPreference, never()).setVisible(true); } }