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 Uri mUri; 61 private final ContentResolver mResolver; 62 private final SettingObserver mSettingObserver; 63 private final BroadcastReceiver mBroadcastReceiver; 64 65 private volatile boolean mAvoidBadWifi = true; 66 AvoidBadWifiTracker(Context ctx, Handler handler)67 public AvoidBadWifiTracker(Context ctx, Handler handler) { 68 this(ctx, handler, null); 69 } 70 AvoidBadWifiTracker(Context ctx, Handler handler, Runnable cb)71 public AvoidBadWifiTracker(Context ctx, Handler handler, Runnable cb) { 72 mContext = ctx; 73 mHandler = handler; 74 mReevaluateRunnable = () -> { if (update() && cb != null) cb.run(); }; 75 mUri = Settings.Global.getUriFor(NETWORK_AVOID_BAD_WIFI); 76 mResolver = mContext.getContentResolver(); 77 mSettingObserver = new SettingObserver(); 78 mBroadcastReceiver = new BroadcastReceiver() { 79 @Override 80 public void onReceive(Context context, Intent intent) { 81 reevaluate(); 82 } 83 }; 84 85 update(); 86 } 87 start()88 public void start() { 89 mResolver.registerContentObserver(mUri, false, mSettingObserver); 90 91 final IntentFilter intentFilter = new IntentFilter(); 92 intentFilter.addAction(Intent.ACTION_CONFIGURATION_CHANGED); 93 mContext.registerReceiverAsUser( 94 mBroadcastReceiver, UserHandle.ALL, intentFilter, null, null); 95 96 reevaluate(); 97 } 98 shutdown()99 public void shutdown() { 100 mResolver.unregisterContentObserver(mSettingObserver); 101 102 mContext.unregisterReceiver(mBroadcastReceiver); 103 } 104 currentValue()105 public boolean currentValue() { 106 return mAvoidBadWifi; 107 } 108 109 /** 110 * Whether the device or carrier configuration disables avoiding bad wifi by default. 111 */ configRestrictsAvoidBadWifi()112 public boolean configRestrictsAvoidBadWifi() { 113 return (mContext.getResources().getInteger(R.integer.config_networkAvoidBadWifi) == 0); 114 } 115 116 /** 117 * Whether we should display a notification when wifi becomes unvalidated. 118 */ shouldNotifyWifiUnvalidated()119 public boolean shouldNotifyWifiUnvalidated() { 120 return configRestrictsAvoidBadWifi() && getSettingsValue() == null; 121 } 122 getSettingsValue()123 public String getSettingsValue() { 124 return Settings.Global.getString(mResolver, NETWORK_AVOID_BAD_WIFI); 125 } 126 127 @VisibleForTesting reevaluate()128 public void reevaluate() { 129 mHandler.post(mReevaluateRunnable); 130 } 131 update()132 public boolean update() { 133 final boolean settingAvoidBadWifi = "1".equals(getSettingsValue()); 134 final boolean prev = mAvoidBadWifi; 135 mAvoidBadWifi = settingAvoidBadWifi || !configRestrictsAvoidBadWifi(); 136 return mAvoidBadWifi != prev; 137 } 138 139 private class SettingObserver extends ContentObserver { SettingObserver()140 public SettingObserver() { 141 super(null); 142 } 143 144 @Override onChange(boolean selfChange)145 public void onChange(boolean selfChange) { 146 Slog.wtf(TAG, "Should never be reached."); 147 } 148 149 @Override onChange(boolean selfChange, Uri uri)150 public void onChange(boolean selfChange, Uri uri) { 151 if (!mUri.equals(uri)) return; 152 reevaluate(); 153 } 154 } 155 } 156