• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2021 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.android.settings.network;
18 
19 import android.content.BroadcastReceiver;
20 import android.content.Context;
21 import android.content.Intent;
22 import android.content.IntentFilter;
23 import android.net.wifi.WifiManager;
24 import android.util.Log;
25 
26 import androidx.annotation.VisibleForTesting;
27 import androidx.lifecycle.Lifecycle;
28 import androidx.lifecycle.LifecycleObserver;
29 import androidx.lifecycle.OnLifecycleEvent;
30 import androidx.preference.Preference;
31 import androidx.preference.PreferenceCategory;
32 
33 import com.android.settingslib.connectivity.ConnectivitySubsystemsRecoveryManager;
34 import com.android.settingslib.utils.HandlerInjector;
35 
36 import java.lang.ref.WeakReference;
37 import java.util.ArrayList;
38 import java.util.List;
39 
40 /**
41  * Helper class to restart connectivity for all requested subsystems.
42  */
43 public class InternetResetHelper implements LifecycleObserver {
44 
45     protected static final String TAG = "InternetResetHelper";
46     public static final long RESTART_TIMEOUT_MS = 15_000; // 15 seconds
47 
48     protected final Context mContext;
49     protected Preference mResettingPreference;
50     protected NetworkMobileProviderController mMobileNetworkController;
51     protected Preference mWifiTogglePreferences;
52     protected List<PreferenceCategory> mWifiNetworkPreferences =
53             new ArrayList<PreferenceCategory>();
54 
55     protected final WifiManager mWifiManager;
56     protected final IntentFilter mWifiStateFilter;
57     protected final BroadcastReceiver mWifiStateReceiver = new BroadcastReceiver() {
58         @Override
59         public void onReceive(Context context, Intent intent) {
60             updateWifiStateChange();
61         }
62     };
63 
64     protected RecoveryWorker mRecoveryWorker;
65     protected boolean mIsWifiReady = true;
66     protected HandlerInjector mHandlerInjector;
67     protected final Runnable mTimeoutRunnable = () -> {
68         Log.w(TAG, "Resume preferences due to connectivity subsystems recovery timed out.");
69         mRecoveryWorker.clearRecovering();
70         mIsWifiReady = true;
71         resumePreferences();
72     };
73 
InternetResetHelper(Context context, Lifecycle lifecycle, NetworkMobileProviderController mobileNetworkController, Preference wifiTogglePreferences, PreferenceCategory connectedWifiEntryPreferenceCategory, PreferenceCategory firstWifiEntryPreferenceCategory, PreferenceCategory wifiEntryPreferenceCategory, Preference resettingPreference)74     public InternetResetHelper(Context context, Lifecycle lifecycle,
75             NetworkMobileProviderController mobileNetworkController,
76             Preference wifiTogglePreferences,
77             PreferenceCategory connectedWifiEntryPreferenceCategory,
78             PreferenceCategory firstWifiEntryPreferenceCategory,
79             PreferenceCategory wifiEntryPreferenceCategory,
80             Preference resettingPreference) {
81         mContext = context;
82         mMobileNetworkController = mobileNetworkController;
83         mWifiTogglePreferences = wifiTogglePreferences;
84         mWifiNetworkPreferences.add(connectedWifiEntryPreferenceCategory);
85         mWifiNetworkPreferences.add(firstWifiEntryPreferenceCategory);
86         mWifiNetworkPreferences.add(wifiEntryPreferenceCategory);
87         mResettingPreference = resettingPreference;
88 
89         mHandlerInjector = new HandlerInjector(context.getMainThreadHandler());
90         mWifiManager = mContext.getSystemService(WifiManager.class);
91         mWifiStateFilter = new IntentFilter(WifiManager.NETWORK_STATE_CHANGED_ACTION);
92         mRecoveryWorker = RecoveryWorker.getInstance(mContext, this);
93 
94         if (lifecycle != null) {
95             lifecycle.addObserver(this);
96         }
97     }
98 
99     /** @OnLifecycleEvent(Lifecycle.Event.ON_RESUME) */
100     @OnLifecycleEvent(Lifecycle.Event.ON_RESUME)
onResume()101     public void onResume() {
102         mContext.registerReceiver(mWifiStateReceiver, mWifiStateFilter,
103                 Context.RECEIVER_EXPORTED_UNAUDITED);
104     }
105 
106     /** @OnLifecycleEvent(Lifecycle.Event.ON_PAUSE) */
107     @OnLifecycleEvent(Lifecycle.Event.ON_PAUSE)
onPause()108     public void onPause() {
109         mContext.unregisterReceiver(mWifiStateReceiver);
110     }
111 
112     /** @OnLifecycleEvent(Lifecycle.Event.ON_DESTROY) */
113     @OnLifecycleEvent(Lifecycle.Event.ON_DESTROY)
onDestroy()114     public void onDestroy() {
115         mHandlerInjector.removeCallbacks(mTimeoutRunnable);
116     }
117 
118     @VisibleForTesting
updateWifiStateChange()119     protected void updateWifiStateChange() {
120         if (!mIsWifiReady && mWifiManager.isWifiEnabled()) {
121             Log.d(TAG, "The Wi-Fi subsystem is done for recovery.");
122             mIsWifiReady = true;
123             resumePreferences();
124         }
125     }
126 
suspendPreferences()127     protected void suspendPreferences() {
128         Log.d(TAG, "Suspend the subsystem preferences");
129         if (mMobileNetworkController != null) {
130             mMobileNetworkController.hidePreference(true /* hide */, true /* immediately */);
131         }
132         if (mWifiTogglePreferences != null) {
133             mWifiTogglePreferences.setVisible(false);
134         }
135         for (PreferenceCategory pref : mWifiNetworkPreferences) {
136             pref.removeAll();
137             pref.setVisible(false);
138         }
139         if (mResettingPreference != null) {
140             mResettingPreference.setVisible(true);
141         }
142     }
143 
resumePreferences()144     protected void resumePreferences() {
145         boolean isRecoveryReady = !mRecoveryWorker.isRecovering();
146         if (isRecoveryReady && mMobileNetworkController != null) {
147             Log.d(TAG, "Resume the Mobile Network controller");
148             mMobileNetworkController.hidePreference(false /* hide */, true /* immediately */);
149         }
150         if (mIsWifiReady && mWifiTogglePreferences != null) {
151             Log.d(TAG, "Resume the Wi-Fi preferences");
152             mWifiTogglePreferences.setVisible(true);
153             for (PreferenceCategory pref : mWifiNetworkPreferences) {
154                 pref.setVisible(true);
155             }
156         }
157         if (isRecoveryReady && mIsWifiReady) {
158             mHandlerInjector.removeCallbacks(mTimeoutRunnable);
159             if (mResettingPreference != null) {
160                 Log.d(TAG, "Resume the Resetting preference");
161                 mResettingPreference.setVisible(false);
162             }
163         }
164     }
165 
showResettingAndSendTimeoutChecks()166     protected void showResettingAndSendTimeoutChecks() {
167         suspendPreferences();
168         mHandlerInjector.postDelayed(mTimeoutRunnable, RESTART_TIMEOUT_MS);
169     }
170 
171     /** Restart connectivity for all requested subsystems. */
restart()172     public void restart() {
173         if (!mRecoveryWorker.isRecoveryAvailable()) {
174             Log.e(TAG, "The connectivity subsystem is not available to restart.");
175             return;
176         }
177         showResettingAndSendTimeoutChecks();
178         mIsWifiReady = !mWifiManager.isWifiEnabled();
179         mRecoveryWorker.triggerRestart();
180     }
181 
182     /** Check if the connectivity subsystem is under recovering. */
checkRecovering()183     public void checkRecovering() {
184         if (!mRecoveryWorker.isRecovering()) return;
185         mIsWifiReady = false;
186         showResettingAndSendTimeoutChecks();
187     }
188 
189     /**
190      * This is a singleton class for ConnectivitySubsystemsRecoveryManager worker.
191      */
192     @VisibleForTesting
193     public static class RecoveryWorker implements
194             ConnectivitySubsystemsRecoveryManager.RecoveryStatusCallback {
195         private static final String TAG = "RecoveryWorker";
196         private static RecoveryWorker sInstance;
197         private static WeakReference<InternetResetHelper> sCallback;
198         private static ConnectivitySubsystemsRecoveryManager sRecoveryManager;
199         private static boolean sIsRecovering;
200 
201         /**
202          * Create a singleton class for ConnectivitySubsystemsRecoveryManager.
203          *
204          * @param context  The context to use for the content resolver.
205          * @param callback The callback of {@link InternetResetHelper} object.
206          * @return an instance of {@link RecoveryWorker} object.
207          */
getInstance(Context context, InternetResetHelper callback)208         public static RecoveryWorker getInstance(Context context, InternetResetHelper callback) {
209             sCallback = new WeakReference<>(callback);
210             if (sInstance != null) return sInstance;
211 
212             sInstance = new RecoveryWorker();
213             Context appContext = context.getApplicationContext();
214             sRecoveryManager = new ConnectivitySubsystemsRecoveryManager(appContext,
215                     appContext.getMainThreadHandler());
216             return sInstance;
217         }
218 
219         /** Returns true, If the subsystem service is recovering. */
isRecovering()220         public boolean isRecovering() {
221             return sIsRecovering;
222         }
223 
224         /** Clear the recovering flag. */
clearRecovering()225         public void clearRecovering() {
226             sIsRecovering = false;
227         }
228 
229         /** Returns true, If the subsystem service is recovery available. */
isRecoveryAvailable()230         public boolean isRecoveryAvailable() {
231             return sRecoveryManager.isRecoveryAvailable();
232         }
233 
234         /** Trigger connectivity recovery for all requested technologies. */
triggerRestart()235         public boolean triggerRestart() {
236             if (!isRecoveryAvailable()) {
237                 Log.e(TAG, "The connectivity subsystem is not available to restart.");
238                 return false;
239             }
240             sIsRecovering = true;
241             sRecoveryManager.triggerSubsystemRestart(null /* reason */, sInstance);
242             Log.d(TAG, "The connectivity subsystem is restarting for recovery.");
243             return true;
244         }
245 
246         @Override
onSubsystemRestartOperationBegin()247         public void onSubsystemRestartOperationBegin() {
248             Log.d(TAG, "The connectivity subsystem is starting for recovery.");
249             sIsRecovering = true;
250         }
251 
252         @Override
onSubsystemRestartOperationEnd()253         public void onSubsystemRestartOperationEnd() {
254             Log.d(TAG, "The connectivity subsystem is done for recovery.");
255             sIsRecovering = false;
256             InternetResetHelper callback = sCallback.get();
257             if (callback == null) return;
258             callback.resumePreferences();
259         }
260     }
261 }
262