2 * Copyright (C) 2016 The Android Open Source Project
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
8 * http://www.apache.org/licenses/LICENSE-2.0
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.
17 package android.net.util;
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.ConnectivityManager;
26 import android.net.Uri;
27 import android.os.Handler;
28 import android.os.Message;
29 import android.os.UserHandle;
30 import android.provider.Settings;
31 import android.util.Slog;
33 import java.util.Arrays;
34 import java.util.List;
36 import com.android.internal.annotations.VisibleForTesting;
37 import com.android.internal.R;
39 import static android.provider.Settings.Global.NETWORK_AVOID_BAD_WIFI;
40 import static android.provider.Settings.Global.NETWORK_METERED_MULTIPATH_PREFERENCE;
43 * A class to encapsulate management of the "Smart Networking" capability of
44 * avoiding bad Wi-Fi when, for example upstream connectivity is lost or
45 * certain critical link failures occur.
47 * This enables the device to switch to another form of connectivity, like
48 * mobile, if it's available and working.
50 * The Runnable |avoidBadWifiCallback|, if given, is posted to the supplied
51 * Handler' whenever the computed "avoid bad wifi" value changes.
53 * Disabling this reverts the device to a level of networking sophistication
54 * circa 2012-13 by disabling disparate code paths each of which contribute to
55 * maintaining continuous, working Internet connectivity.
59 public class MultinetworkPolicyTracker {
60 private static String TAG = MultinetworkPolicyTracker.class.getSimpleName();
62 private final Context mContext;
63 private final Handler mHandler;
64 private final Runnable mReevaluateRunnable;
65 private final List<Uri> mSettingsUris;
66 private final ContentResolver mResolver;
67 private final SettingObserver mSettingObserver;
68 private final BroadcastReceiver mBroadcastReceiver;
70 private volatile boolean mAvoidBadWifi = true;
71 private volatile int mMeteredMultipathPreference;
73 public MultinetworkPolicyTracker(Context ctx, Handler handler) {
74 this(ctx, handler, null);
77 public MultinetworkPolicyTracker(Context ctx, Handler handler, Runnable avoidBadWifiCallback) {
80 mReevaluateRunnable = () -> {
81 if (updateAvoidBadWifi() && avoidBadWifiCallback != null) {
82 avoidBadWifiCallback.run();
84 updateMeteredMultipathPreference();
86 mSettingsUris = Arrays.asList(
87 Settings.Global.getUriFor(NETWORK_AVOID_BAD_WIFI),
88 Settings.Global.getUriFor(NETWORK_METERED_MULTIPATH_PREFERENCE));
89 mResolver = mContext.getContentResolver();
90 mSettingObserver = new SettingObserver();
91 mBroadcastReceiver = new BroadcastReceiver() {
93 public void onReceive(Context context, Intent intent) {
99 updateMeteredMultipathPreference();
102 public void start() {
103 for (Uri uri : mSettingsUris) {
104 mResolver.registerContentObserver(uri, false, mSettingObserver);
107 final IntentFilter intentFilter = new IntentFilter();
108 intentFilter.addAction(Intent.ACTION_CONFIGURATION_CHANGED);
109 mContext.registerReceiverAsUser(
110 mBroadcastReceiver, UserHandle.ALL, intentFilter, null, null);
115 public void shutdown() {
116 mResolver.unregisterContentObserver(mSettingObserver);
118 mContext.unregisterReceiver(mBroadcastReceiver);
121 public boolean getAvoidBadWifi() {
122 return mAvoidBadWifi;
125 // TODO: move this to MultipathPolicyTracker.
126 public int getMeteredMultipathPreference() {
127 return mMeteredMultipathPreference;
131 * Whether the device or carrier configuration disables avoiding bad wifi by default.
133 public boolean configRestrictsAvoidBadWifi() {
134 return (mContext.getResources().getInteger(R.integer.config_networkAvoidBadWifi) == 0);
138 * Whether we should display a notification when wifi becomes unvalidated.
140 public boolean shouldNotifyWifiUnvalidated() {
141 return configRestrictsAvoidBadWifi() && getAvoidBadWifiSetting() == null;
144 public String getAvoidBadWifiSetting() {
145 return Settings.Global.getString(mResolver, NETWORK_AVOID_BAD_WIFI);
149 public void reevaluate() {
150 mHandler.post(mReevaluateRunnable);
153 public boolean updateAvoidBadWifi() {
154 final boolean settingAvoidBadWifi = "1".equals(getAvoidBadWifiSetting());
155 final boolean prev = mAvoidBadWifi;
156 mAvoidBadWifi = settingAvoidBadWifi || !configRestrictsAvoidBadWifi();
157 return mAvoidBadWifi != prev;
161 * The default (device and carrier-dependent) value for metered multipath preference.
163 public int configMeteredMultipathPreference() {
164 return mContext.getResources().getInteger(
165 R.integer.config_networkMeteredMultipathPreference);
168 public void updateMeteredMultipathPreference() {
169 String setting = Settings.Global.getString(mResolver, NETWORK_METERED_MULTIPATH_PREFERENCE);
171 mMeteredMultipathPreference = Integer.parseInt(setting);
172 } catch (NumberFormatException e) {
173 mMeteredMultipathPreference = configMeteredMultipathPreference();
177 private class SettingObserver extends ContentObserver {
178 public SettingObserver() {
183 public void onChange(boolean selfChange) {
184 Slog.wtf(TAG, "Should never be reached.");
188 public void onChange(boolean selfChange, Uri uri) {
189 if (!mSettingsUris.contains(uri)) {
190 Slog.wtf(TAG, "Unexpected settings observation: " + uri);