| /* |
| * Copyright (C) 2016 The Android Open Source Project |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| package android.net.util; |
| |
| import android.content.BroadcastReceiver; |
| import android.content.ContentResolver; |
| import android.content.Context; |
| import android.content.Intent; |
| import android.content.IntentFilter; |
| import android.database.ContentObserver; |
| import android.net.Uri; |
| import android.os.Handler; |
| import android.os.Message; |
| import android.os.UserHandle; |
| import android.provider.Settings; |
| import android.util.Slog; |
| |
| import com.android.internal.annotations.VisibleForTesting; |
| import com.android.internal.R; |
| |
| import static android.provider.Settings.Global.NETWORK_AVOID_BAD_WIFI; |
| |
| /** |
| * A class to encapsulate management of the "Smart Networking" capability of |
| * avoiding bad Wi-Fi when, for example upstream connectivity is lost or |
| * certain critical link failures occur. |
| * |
| * This enables the device to switch to another form of connectivity, like |
| * mobile, if it's available and working. |
| * |
| * The Runnable |cb|, if given, is called on the supplied Handler's thread |
| * whether the computed "avoid bad wifi" value changes. |
| * |
| * Disabling this reverts the device to a level of networking sophistication |
| * circa 2012-13 by disabling disparate code paths each of which contribute to |
| * maintaining continuous, working Internet connectivity. |
| * |
| * @hide |
| */ |
| public class AvoidBadWifiTracker { |
| private static String TAG = AvoidBadWifiTracker.class.getSimpleName(); |
| |
| private final Context mContext; |
| private final Handler mHandler; |
| private final Runnable mReevaluateRunnable; |
| private final Uri mUri; |
| private final ContentResolver mResolver; |
| private final SettingObserver mSettingObserver; |
| private final BroadcastReceiver mBroadcastReceiver; |
| |
| private volatile boolean mAvoidBadWifi = true; |
| |
| public AvoidBadWifiTracker(Context ctx, Handler handler) { |
| this(ctx, handler, null); |
| } |
| |
| public AvoidBadWifiTracker(Context ctx, Handler handler, Runnable cb) { |
| mContext = ctx; |
| mHandler = handler; |
| mReevaluateRunnable = () -> { if (update() && cb != null) cb.run(); }; |
| mUri = Settings.Global.getUriFor(NETWORK_AVOID_BAD_WIFI); |
| mResolver = mContext.getContentResolver(); |
| mSettingObserver = new SettingObserver(); |
| mBroadcastReceiver = new BroadcastReceiver() { |
| @Override |
| public void onReceive(Context context, Intent intent) { |
| reevaluate(); |
| } |
| }; |
| |
| update(); |
| } |
| |
| public void start() { |
| mResolver.registerContentObserver(mUri, false, mSettingObserver); |
| |
| final IntentFilter intentFilter = new IntentFilter(); |
| intentFilter.addAction(Intent.ACTION_CONFIGURATION_CHANGED); |
| mContext.registerReceiverAsUser( |
| mBroadcastReceiver, UserHandle.ALL, intentFilter, null, null); |
| |
| reevaluate(); |
| } |
| |
| public void shutdown() { |
| mResolver.unregisterContentObserver(mSettingObserver); |
| |
| mContext.unregisterReceiver(mBroadcastReceiver); |
| } |
| |
| public boolean currentValue() { |
| return mAvoidBadWifi; |
| } |
| |
| /** |
| * Whether the device or carrier configuration disables avoiding bad wifi by default. |
| */ |
| public boolean configRestrictsAvoidBadWifi() { |
| return (mContext.getResources().getInteger(R.integer.config_networkAvoidBadWifi) == 0); |
| } |
| |
| /** |
| * Whether we should display a notification when wifi becomes unvalidated. |
| */ |
| public boolean shouldNotifyWifiUnvalidated() { |
| return configRestrictsAvoidBadWifi() && getSettingsValue() == null; |
| } |
| |
| public String getSettingsValue() { |
| return Settings.Global.getString(mResolver, NETWORK_AVOID_BAD_WIFI); |
| } |
| |
| @VisibleForTesting |
| public void reevaluate() { |
| mHandler.post(mReevaluateRunnable); |
| } |
| |
| public boolean update() { |
| final boolean settingAvoidBadWifi = "1".equals(getSettingsValue()); |
| final boolean prev = mAvoidBadWifi; |
| mAvoidBadWifi = settingAvoidBadWifi || !configRestrictsAvoidBadWifi(); |
| return mAvoidBadWifi != prev; |
| } |
| |
| private class SettingObserver extends ContentObserver { |
| public SettingObserver() { |
| super(null); |
| } |
| |
| @Override |
| public void onChange(boolean selfChange) { |
| Slog.wtf(TAG, "Should never be reached."); |
| } |
| |
| @Override |
| public void onChange(boolean selfChange, Uri uri) { |
| if (!mUri.equals(uri)) return; |
| reevaluate(); |
| } |
| } |
| } |