1 /* 2 * Copyright (C) 2016 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 android.net.util; 18 19 import android.content.BroadcastReceiver; 20 import android.content.ContentResolver; 21 import android.content.Context; 22 import android.content.Intent; 23 import android.content.IntentFilter; 24 import android.database.ContentObserver; 25 import android.net.Uri; 26 import android.os.Handler; 27 import android.os.Message; 28 import android.os.UserHandle; 29 import android.provider.Settings; 30 import android.util.Slog; 31 32 import com.android.internal.annotations.VisibleForTesting; 33 import com.android.internal.R; 34 35 import static android.provider.Settings.Global.NETWORK_AVOID_BAD_WIFI; 36 37 /** 38 * A class to encapsulate management of the "Smart Networking" capability of 39 * avoiding bad Wi-Fi when, for example upstream connectivity is lost or 40 * certain critical link failures occur. 41 * 42 * This enables the device to switch to another form of connectivity, like 43 * mobile, if it's available and working. 44 * 45 * The Runnable |cb|, if given, is called on the supplied Handler's thread 46 * whether the computed "avoid bad wifi" value changes. 47 * 48 * Disabling this reverts the device to a level of networking sophistication 49 * circa 2012-13 by disabling disparate code paths each of which contribute to 50 * maintaining continuous, working Internet connectivity. 51 * 52 * @hide 53 */ 54 public class AvoidBadWifiTracker { 55 private static String TAG = AvoidBadWifiTracker.class.getSimpleName(); 56 57 private final Context mContext; 58 private final Handler mHandler; 59 private final Runnable mReevaluateRunnable; 60 private final SettingObserver mSettingObserver; 61 private volatile boolean mAvoidBadWifi = true; 62 AvoidBadWifiTracker(Context ctx, Handler handler)63 public AvoidBadWifiTracker(Context ctx, Handler handler) { 64 this(ctx, handler, null); 65 } 66 AvoidBadWifiTracker(Context ctx, Handler handler, Runnable cb)67 public AvoidBadWifiTracker(Context ctx, Handler handler, Runnable cb) { 68 mContext = ctx; 69 mHandler = handler; 70 mReevaluateRunnable = () -> { if (update() && cb != null) cb.run(); }; 71 mSettingObserver = new SettingObserver(); 72 73 final IntentFilter intentFilter = new IntentFilter(); 74 intentFilter.addAction(Intent.ACTION_CONFIGURATION_CHANGED); 75 mContext.registerReceiverAsUser(new BroadcastReceiver() { 76 public void onReceive(Context context, Intent intent) { 77 reevaluate(); 78 } 79 }, UserHandle.ALL, intentFilter, null, null); 80 81 update(); 82 } 83 currentValue()84 public boolean currentValue() { 85 return mAvoidBadWifi; 86 } 87 88 /** 89 * Whether the device or carrier configuration disables avoiding bad wifi by default. 90 */ configRestrictsAvoidBadWifi()91 public boolean configRestrictsAvoidBadWifi() { 92 return (mContext.getResources().getInteger(R.integer.config_networkAvoidBadWifi) == 0); 93 } 94 95 /** 96 * Whether we should display a notification when wifi becomes unvalidated. 97 */ shouldNotifyWifiUnvalidated()98 public boolean shouldNotifyWifiUnvalidated() { 99 return configRestrictsAvoidBadWifi() && getSettingsValue() == null; 100 } 101 getSettingsValue()102 public String getSettingsValue() { 103 final ContentResolver resolver = mContext.getContentResolver(); 104 return Settings.Global.getString(resolver, NETWORK_AVOID_BAD_WIFI); 105 } 106 107 @VisibleForTesting reevaluate()108 public void reevaluate() { 109 mHandler.post(mReevaluateRunnable); 110 } 111 update()112 public boolean update() { 113 final boolean settingAvoidBadWifi = "1".equals(getSettingsValue()); 114 final boolean prev = mAvoidBadWifi; 115 mAvoidBadWifi = settingAvoidBadWifi || !configRestrictsAvoidBadWifi(); 116 return mAvoidBadWifi != prev; 117 } 118 119 private class SettingObserver extends ContentObserver { 120 private final Uri mUri = Settings.Global.getUriFor(NETWORK_AVOID_BAD_WIFI); 121 SettingObserver()122 public SettingObserver() { 123 super(null); 124 final ContentResolver resolver = mContext.getContentResolver(); 125 resolver.registerContentObserver(mUri, false, this); 126 } 127 128 @Override onChange(boolean selfChange)129 public void onChange(boolean selfChange) { 130 Slog.wtf(TAG, "Should never be reached."); 131 } 132 133 @Override onChange(boolean selfChange, Uri uri)134 public void onChange(boolean selfChange, Uri uri) { 135 if (!mUri.equals(uri)) return; 136 reevaluate(); 137 } 138 } 139 } 140