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.content.BroadcastReceiver; 28 import android.content.Context; 29 import android.content.Intent; 30 import android.content.IntentFilter; 31 import android.net.ConnectivityManager; 32 import android.net.ConnectivityManager.NetworkCallback; 33 import android.net.Network; 34 import android.net.NetworkCapabilities; 35 import android.net.wifi.WifiInfo; 36 import android.net.wifi.WifiManager; 37 import android.util.Log; 38 39 import androidx.annotation.NonNull; 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 Context.RECEIVER_EXPORTED_UNAUDITED); 187 } 188 189 /** @OnLifecycleEvent(ON_PAUSE) */ 190 @OnLifecycleEvent(ON_PAUSE) onPause()191 public void onPause() { 192 mAirplaneModeEnabler.stop(); 193 mConnectivityManager.unregisterNetworkCallback(mNetworkCallback); 194 mContext.unregisterReceiver(mWifiStateReceiver); 195 } 196 197 /** @OnLifecycleEvent(ON_DESTROY) */ 198 @OnLifecycleEvent(ON_DESTROY) onDestroy()199 public void onDestroy() { 200 mAirplaneModeEnabler.close(); 201 } 202 203 @Override onAirplaneModeChanged(boolean isAirplaneModeOn)204 public void onAirplaneModeChanged(boolean isAirplaneModeOn) { 205 fetchActiveNetwork(); 206 if (mListener != null) { 207 mListener.onAirplaneModeChanged(isAirplaneModeOn); 208 } 209 } 210 fetchActiveNetwork()211 private void fetchActiveNetwork() { 212 Network activeNetwork = mConnectivityManager.getActiveNetwork(); 213 if (activeNetwork == null) { 214 mInternetAvailable = false; 215 updateInternetType(); 216 return; 217 } 218 219 NetworkCapabilities activeNetworkCapabilities = 220 mConnectivityManager.getNetworkCapabilities(activeNetwork); 221 if (activeNetworkCapabilities == null) { 222 mInternetAvailable = false; 223 updateInternetType(); 224 return; 225 } 226 227 updateInternetAvailable(activeNetworkCapabilities); 228 } 229 230 @VisibleForTesting updateInternetAvailable(@onNull NetworkCapabilities capabilities)231 void updateInternetAvailable(@NonNull NetworkCapabilities capabilities) { 232 boolean internetAvailable = false; 233 if (capabilities.hasCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET) 234 && capabilities.hasCapability(NetworkCapabilities.NET_CAPABILITY_VALIDATED)) { 235 for (int transport : capabilities.getTransportTypes()) { 236 if (sTransportMap.containsKey(transport)) { 237 mTransport = transport; 238 internetAvailable = true; 239 Log.i(TAG, "Detect an internet available network with transport type: " 240 + mTransport); 241 break; 242 } 243 } 244 } 245 mInternetAvailable = internetAvailable; 246 updateInternetType(); 247 } 248 249 @VisibleForTesting updateInternetType()250 void updateInternetType() { 251 @InternetType int internetType = INTERNET_NETWORKS_AVAILABLE; 252 if (mInternetAvailable) { 253 internetType = sTransportMap.get(mTransport); 254 if (internetType == INTERNET_WIFI && isCarrierWifiActive()) { 255 internetType = INTERNET_CELLULAR; 256 } 257 } else if (mAirplaneModeEnabler.isAirplaneModeOn() 258 && mWifiManager.getWifiState() != WifiManager.WIFI_STATE_ENABLED) { 259 internetType = INTERNET_OFF; 260 } 261 mInternetType = internetType; 262 263 if (mListener != null) { 264 mListener.onInternetTypeChanged(mInternetType); 265 } 266 } 267 isCarrierWifiActive()268 protected boolean isCarrierWifiActive() { 269 final WifiInfo wifiInfo = mWifiManager.getConnectionInfo(); 270 if (wifiInfo == null || !wifiInfo.isCarrierMerged()) { 271 return false; 272 } 273 Log.i(TAG, "Detect a merged carrier Wi-Fi connected."); 274 return true; 275 } 276 277 /** 278 * Get the internet type. 279 */ getInternetType()280 public @InternetType int getInternetType() { 281 return mInternetType; 282 } 283 284 /** 285 * Return ture when the airplane mode is on. 286 */ isAirplaneModeOn()287 public boolean isAirplaneModeOn() { 288 return mAirplaneModeEnabler.isAirplaneModeOn(); 289 } 290 291 /** 292 * Return ture when the Wi-Fi is enabled. 293 */ isWifiEnabled()294 public boolean isWifiEnabled() { 295 return mWifiManager.isWifiEnabled(); 296 } 297 } 298