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 static android.net.NetworkCapabilities.TRANSPORT_CELLULAR; 20 import static android.net.NetworkCapabilities.TRANSPORT_ETHERNET; 21 import static android.net.NetworkCapabilities.TRANSPORT_WIFI; 22 23 import static androidx.lifecycle.Lifecycle.Event.ON_DESTROY; 24 import static androidx.lifecycle.Lifecycle.Event.ON_PAUSE; 25 import static androidx.lifecycle.Lifecycle.Event.ON_RESUME; 26 27 import android.annotation.NonNull; 28 import android.content.BroadcastReceiver; 29 import android.content.Context; 30 import android.content.Intent; 31 import android.content.IntentFilter; 32 import android.net.ConnectivityManager; 33 import android.net.ConnectivityManager.NetworkCallback; 34 import android.net.Network; 35 import android.net.NetworkCapabilities; 36 import android.net.wifi.WifiInfo; 37 import android.net.wifi.WifiManager; 38 import android.util.Log; 39 40 import androidx.annotation.VisibleForTesting; 41 import androidx.lifecycle.Lifecycle; 42 import androidx.lifecycle.LifecycleObserver; 43 import androidx.lifecycle.OnLifecycleEvent; 44 45 import com.android.settings.AirplaneModeEnabler; 46 47 import java.lang.annotation.Retention; 48 import java.lang.annotation.RetentionPolicy; 49 import java.util.HashMap; 50 import java.util.Map; 51 52 /** 53 * Helper class to update the internet type for connected network preference 54 */ 55 public class InternetUpdater implements AirplaneModeEnabler.OnAirplaneModeChangedListener, 56 LifecycleObserver { 57 58 private static final String TAG = "InternetUpdater"; 59 60 private InternetChangeListener mListener; 61 62 /** Interface that handles the internet updater callback */ 63 public interface InternetChangeListener { 64 /** 65 * Called when internet type is changed. 66 * 67 * @param internetType the internet type 68 */ onInternetTypeChanged(@nternetType int internetType)69 default void onInternetTypeChanged(@InternetType int internetType) {}; 70 71 /** 72 * Called when airplane mode state is changed. 73 */ onAirplaneModeChanged(boolean isAirplaneModeOn)74 default void onAirplaneModeChanged(boolean isAirplaneModeOn) {}; 75 76 /** 77 * Called when Wi-Fi enabled is changed. 78 */ onWifiEnabledChanged(boolean enabled)79 default void onWifiEnabledChanged(boolean enabled) {}; 80 } 81 82 /** 83 * Indicates the internet is off when airplane mode is on. 84 */ 85 public static final int INTERNET_OFF = 0; 86 87 /** 88 * Indicates this internet is not connected (includes no networks connected) or network(s) 89 * available. 90 * 91 * Examples include: 92 * <p>When airplane mode is turned off, and some networks (Wi-Fi, Mobile-data) are turned on, 93 * but no network can access the Internet. 94 * 95 * <p>When the airplane mode is turned on, and the WiFi is also turned on, but the WiFi is not 96 * connected or cannot access the Internet. 97 */ 98 public static final int INTERNET_NETWORKS_AVAILABLE = 1; 99 100 /** 101 * Indicates this internet uses a Wi-Fi network type. 102 */ 103 public static final int INTERNET_WIFI = 2; 104 105 /** 106 * Indicates this internet uses a Cellular network type. 107 */ 108 public static final int INTERNET_CELLULAR = 3; 109 110 /** 111 * Indicates this internet uses a Ethernet network type. 112 */ 113 public static final int INTERNET_ETHERNET = 4; 114 115 @Retention(RetentionPolicy.SOURCE) 116 @android.annotation.IntDef(prefix = { "INTERNET_" }, value = { 117 INTERNET_OFF, 118 INTERNET_NETWORKS_AVAILABLE, 119 INTERNET_WIFI, 120 INTERNET_CELLULAR, 121 INTERNET_ETHERNET, 122 }) 123 public @interface InternetType { } 124 private @InternetType int mInternetType; 125 126 private final Context mContext; 127 private final ConnectivityManager mConnectivityManager; 128 private final WifiManager mWifiManager; 129 private final IntentFilter mWifiStateFilter; 130 @VisibleForTesting 131 AirplaneModeEnabler mAirplaneModeEnabler; 132 133 @VisibleForTesting 134 boolean mInternetAvailable; 135 @VisibleForTesting 136 int mTransport; 137 private static Map<Integer, Integer> sTransportMap = new HashMap<>(); 138 static { sTransportMap.put(TRANSPORT_WIFI, INTERNET_WIFI)139 sTransportMap.put(TRANSPORT_WIFI, INTERNET_WIFI); sTransportMap.put(TRANSPORT_CELLULAR, INTERNET_CELLULAR)140 sTransportMap.put(TRANSPORT_CELLULAR, INTERNET_CELLULAR); sTransportMap.put(TRANSPORT_ETHERNET, INTERNET_ETHERNET)141 sTransportMap.put(TRANSPORT_ETHERNET, INTERNET_ETHERNET); 142 } 143 144 private NetworkCallback mNetworkCallback = new NetworkCallback() { 145 public void onCapabilitiesChanged(@NonNull Network network, 146 @NonNull NetworkCapabilities networkCapabilities) { 147 updateInternetAvailable(networkCapabilities); 148 } 149 150 @Override 151 public void onLost(@NonNull Network network) { 152 mInternetAvailable = false; 153 updateInternetType(); 154 } 155 }; 156 157 private final BroadcastReceiver mWifiStateReceiver = new BroadcastReceiver() { 158 @Override 159 public void onReceive(Context context, Intent intent) { 160 fetchActiveNetwork(); 161 if (mListener != null) { 162 mListener.onWifiEnabledChanged(mWifiManager.isWifiEnabled()); 163 } 164 } 165 }; 166 InternetUpdater(Context context, Lifecycle lifecycle, InternetChangeListener listener)167 public InternetUpdater(Context context, Lifecycle lifecycle, InternetChangeListener listener) { 168 mContext = context; 169 mAirplaneModeEnabler = new AirplaneModeEnabler(mContext, this); 170 mConnectivityManager = mContext.getSystemService(ConnectivityManager.class); 171 mWifiManager = mContext.getSystemService(WifiManager.class); 172 mWifiStateFilter = new IntentFilter(WifiManager.WIFI_STATE_CHANGED_ACTION); 173 mListener = listener; 174 fetchActiveNetwork(); 175 if (lifecycle != null) { 176 lifecycle.addObserver(this); 177 } 178 } 179 180 /** @OnLifecycleEvent(ON_RESUME) */ 181 @OnLifecycleEvent(ON_RESUME) onResume()182 public void onResume() { 183 mAirplaneModeEnabler.start(); 184 mConnectivityManager.registerDefaultNetworkCallback(mNetworkCallback); 185 mContext.registerReceiver(mWifiStateReceiver, mWifiStateFilter); 186 } 187 188 /** @OnLifecycleEvent(ON_PAUSE) */ 189 @OnLifecycleEvent(ON_PAUSE) onPause()190 public void onPause() { 191 mAirplaneModeEnabler.stop(); 192 mConnectivityManager.unregisterNetworkCallback(mNetworkCallback); 193 mContext.unregisterReceiver(mWifiStateReceiver); 194 } 195 196 /** @OnLifecycleEvent(ON_DESTROY) */ 197 @OnLifecycleEvent(ON_DESTROY) onDestroy()198 public void onDestroy() { 199 mAirplaneModeEnabler.close(); 200 } 201 202 @Override onAirplaneModeChanged(boolean isAirplaneModeOn)203 public void onAirplaneModeChanged(boolean isAirplaneModeOn) { 204 fetchActiveNetwork(); 205 if (mListener != null) { 206 mListener.onAirplaneModeChanged(isAirplaneModeOn); 207 } 208 } 209 fetchActiveNetwork()210 private void fetchActiveNetwork() { 211 Network activeNetwork = mConnectivityManager.getActiveNetwork(); 212 if (activeNetwork == null) { 213 mInternetAvailable = false; 214 updateInternetType(); 215 return; 216 } 217 218 NetworkCapabilities activeNetworkCapabilities = 219 mConnectivityManager.getNetworkCapabilities(activeNetwork); 220 if (activeNetworkCapabilities == null) { 221 mInternetAvailable = false; 222 updateInternetType(); 223 return; 224 } 225 226 updateInternetAvailable(activeNetworkCapabilities); 227 } 228 229 @VisibleForTesting updateInternetAvailable(@onNull NetworkCapabilities capabilities)230 void updateInternetAvailable(@NonNull NetworkCapabilities capabilities) { 231 boolean internetAvailable = false; 232 if (capabilities.hasCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET) 233 && capabilities.hasCapability(NetworkCapabilities.NET_CAPABILITY_VALIDATED)) { 234 for (int transport : capabilities.getTransportTypes()) { 235 if (sTransportMap.containsKey(transport)) { 236 mTransport = transport; 237 internetAvailable = true; 238 Log.i(TAG, "Detect an internet available network with transport type: " 239 + mTransport); 240 break; 241 } 242 } 243 } 244 mInternetAvailable = internetAvailable; 245 updateInternetType(); 246 } 247 248 @VisibleForTesting updateInternetType()249 void updateInternetType() { 250 @InternetType int internetType = INTERNET_NETWORKS_AVAILABLE; 251 if (mInternetAvailable) { 252 internetType = sTransportMap.get(mTransport); 253 if (internetType == INTERNET_WIFI && isCarrierWifiActive()) { 254 internetType = INTERNET_CELLULAR; 255 } 256 } else if (mAirplaneModeEnabler.isAirplaneModeOn() 257 && mWifiManager.getWifiState() != WifiManager.WIFI_STATE_ENABLED) { 258 internetType = INTERNET_OFF; 259 } 260 mInternetType = internetType; 261 262 if (mListener != null) { 263 mListener.onInternetTypeChanged(mInternetType); 264 } 265 } 266 isCarrierWifiActive()267 protected boolean isCarrierWifiActive() { 268 final WifiInfo wifiInfo = mWifiManager.getConnectionInfo(); 269 if (wifiInfo == null || !wifiInfo.isCarrierMerged()) { 270 return false; 271 } 272 Log.i(TAG, "Detect a merged carrier Wi-Fi connected."); 273 return true; 274 } 275 276 /** 277 * Get the internet type. 278 */ getInternetType()279 public @InternetType int getInternetType() { 280 return mInternetType; 281 } 282 283 /** 284 * Return ture when the airplane mode is on. 285 */ isAirplaneModeOn()286 public boolean isAirplaneModeOn() { 287 return mAirplaneModeEnabler.isAirplaneModeOn(); 288 } 289 290 /** 291 * Return ture when the Wi-Fi is enabled. 292 */ isWifiEnabled()293 public boolean isWifiEnabled() { 294 return mWifiManager.isWifiEnabled(); 295 } 296 } 297