1 /* 2 * Copyright (C) 2018 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.google.android.car.kitchensink.connectivity; 18 19 import android.annotation.NonNull; 20 import android.annotation.Nullable; 21 import android.annotation.SuppressLint; 22 import android.bluetooth.BluetoothAdapter; 23 import android.content.Context; 24 import android.content.Intent; 25 import android.location.LocationManager; 26 import android.net.ConnectivityManager; 27 import android.net.ConnectivityManager.NetworkCallback; 28 import android.net.LinkProperties; 29 import android.net.Network; 30 import android.net.NetworkCapabilities; 31 import android.net.NetworkInfo; 32 import android.net.NetworkRequest; 33 import android.net.wifi.WifiConfiguration; 34 import android.net.wifi.WifiManager; 35 import android.os.Bundle; 36 import android.os.Process; 37 import android.os.UserHandle; 38 import android.util.Log; 39 import android.util.SparseArray; 40 import android.view.LayoutInflater; 41 import android.view.View; 42 import android.view.ViewGroup; 43 import android.widget.ListView; 44 import android.widget.TextView; 45 import android.widget.Toast; 46 47 import androidx.fragment.app.Fragment; 48 import androidx.swiperefreshlayout.widget.SwipeRefreshLayout; 49 import androidx.viewpager.widget.ViewPager; 50 51 import com.google.android.car.kitchensink.R; 52 import com.google.android.car.kitchensink.SimplePagerAdapter; 53 54 import java.net.NetworkInterface; 55 import java.net.SocketException; 56 import java.util.Objects; 57 import java.util.Timer; 58 import java.util.TimerTask; 59 60 @SuppressLint("SetTextI18n") 61 public class ConnectivityFragment extends Fragment { 62 private static final String TAG = ConnectivityFragment.class.getSimpleName(); 63 64 private ConnectivityManager mConnectivityManager; 65 private WifiManager mWifiManager; 66 private LocationManager mLocationManager; 67 68 // Sort out current Network objects (NetId -> Network) 69 private SparseArray<Network> mNetworks = new SparseArray<Network>(); 70 71 private TextView mWifiStatusPolled; 72 private TextView mTetheringStatus; 73 private TextView mTetheringStatusPolled; 74 private TextView mLocalOnlyStatus; 75 76 private Timer mWifiUpdater; 77 78 /** 79 * Create our own network callback object to use with NetworkRequests. Contains a reference to 80 * a Network so we can be sure to only surface updates on the network we want to see them on. 81 * We have to do this because there isn't a way to say "give me this SPECIFIC network." There's 82 * only "give me A network with these capabilities/transports." 83 */ 84 public class NetworkByIdCallback extends NetworkCallback { 85 private final Network mNetwork; 86 NetworkByIdCallback(@onNull Network n)87 NetworkByIdCallback(@NonNull Network n) { 88 mNetwork = Objects.requireNonNull(n); 89 } 90 91 @Override onAvailable(Network n)92 public void onAvailable(Network n) { 93 if (mNetwork.equals(n)) { 94 showToast("onAvailable(), netId: " + n); 95 } 96 } 97 98 @Override onLosing(Network n, int maxMsToLive)99 public void onLosing(Network n, int maxMsToLive) { 100 if (mNetwork.equals(n)) { 101 showToast("onLosing(), netId: " + n); 102 } 103 } 104 105 @Override onLost(Network n)106 public void onLost(Network n) { 107 if (mNetwork.equals(n)) { 108 showToast("onLost(), netId: " + n); 109 } 110 } 111 } 112 113 // Map of NetId -> NetworkByIdCallback Objects -- Used to release requested networks 114 SparseArray<NetworkByIdCallback> mNetworkCallbacks = new SparseArray<NetworkByIdCallback>(); 115 116 /** 117 * Implement a swipe-to-refresh list of available networks. NetworkListAdapter takes an array 118 * of NetworkItems that it cascades to the view. SwipeRefreshLayout wraps the adapter. 119 */ 120 public static class NetworkItem { 121 public int mNetId; 122 public String mType; 123 public String mState; 124 public String mConnected; 125 public String mAvailable; 126 public String mRoaming; 127 public String mInterfaceName; 128 public String mHwAddress; 129 public String mIpAddresses; 130 public String mDnsAddresses; 131 public String mDomains; 132 public String mRoutes; 133 public String mTransports; 134 public String mCapabilities; 135 public String mBandwidth; 136 public boolean mDefault; 137 public boolean mRequested; 138 } 139 140 private NetworkItem[] mNetworkItems = new NetworkItem[0]; 141 private NetworkListAdapter mNetworksAdapter; 142 private SwipeRefreshLayout mNetworkListRefresher; 143 144 /** 145 * Builds a NetworkRequest fit to a given network in the hope that we just get updates on that 146 * one network. This is the best way to get single network updates right now, as the request 147 * system works only on transport and capability requirements. There aaaare "network 148 * specifiers" but those only work based on the transport (i.e "eth0" would ask type ETHERNET 149 * for the correct interface where as "GoogleGuest" might ask type WIFI for the Network on SSID 150 * "GoogleGuest"). Ends up being paired with the custom callback above to only surface events 151 * for the specific network in question as well. 152 */ getRequestForNetwork(Network n)153 private NetworkRequest getRequestForNetwork(Network n) { 154 NetworkCapabilities nc = mConnectivityManager.getNetworkCapabilities(n); 155 156 NetworkRequest.Builder b = new NetworkRequest.Builder(); 157 b.clearCapabilities(); 158 159 for (int transportType : nc.getTransportTypes()) { 160 b.addTransportType(transportType); 161 } 162 163 for (int capability : nc.getCapabilities()) { 164 // Not all capabilities are requestable. According to source, all mutable capabilities 165 // except trusted are not requestable. Trying to request them results in an error being 166 // thrown 167 if (isRequestableCapability(capability)) { 168 b.addCapability(capability); 169 } 170 } 171 172 return b.build(); 173 } 174 isRequestableCapability(int c)175 private boolean isRequestableCapability(int c) { 176 return !(c == NetworkCapabilities.NET_CAPABILITY_VALIDATED 177 || c == NetworkCapabilities.NET_CAPABILITY_CAPTIVE_PORTAL 178 || c == NetworkCapabilities.NET_CAPABILITY_NOT_ROAMING 179 || c == NetworkCapabilities.NET_CAPABILITY_FOREGROUND 180 || c == NetworkCapabilities.NET_CAPABILITY_NOT_CONGESTED 181 || c == NetworkCapabilities.NET_CAPABILITY_NOT_SUSPENDED); 182 } 183 requestNetworkById(int netId)184 public void requestNetworkById(int netId) { 185 if (mNetworkCallbacks.get(netId) != null) { 186 return; 187 } 188 189 Network network = mNetworks.get(netId); 190 if (network == null) { 191 return; 192 } 193 194 NetworkRequest request = getRequestForNetwork(network); 195 NetworkByIdCallback cb = new NetworkByIdCallback(network); 196 mNetworkCallbacks.put(netId, cb); 197 mConnectivityManager.requestNetwork(request, cb); 198 showToast("Requesting Network " + netId); 199 } 200 releaseNetworkById(int netId)201 public void releaseNetworkById(int netId) { 202 NetworkByIdCallback cb = mNetworkCallbacks.get(netId); 203 if (cb != null) { 204 mConnectivityManager.unregisterNetworkCallback(cb); 205 mNetworkCallbacks.remove(netId); 206 showToast("Released Network " + netId); 207 } 208 } 209 releaseAllNetworks()210 public void releaseAllNetworks() { 211 for (NetworkItem n : mNetworkItems) { 212 releaseNetworkById(n.mNetId); 213 } 214 } 215 bindToNetwork(int netId)216 public void bindToNetwork(int netId) { 217 Network network = mNetworks.get(netId); 218 if (network == null) { 219 return; 220 } 221 222 Network def = mConnectivityManager.getBoundNetworkForProcess(); 223 if (def != null && def.getNetId() != netId) { 224 clearBoundNetwork(); 225 } 226 mConnectivityManager.bindProcessToNetwork(network); 227 showToast("Set process default network " + netId); 228 } 229 clearBoundNetwork()230 public void clearBoundNetwork() { 231 mConnectivityManager.bindProcessToNetwork(null); 232 showToast("Clear process default network"); 233 } 234 reportNetworkbyId(int netId)235 public void reportNetworkbyId(int netId) { 236 Network network = mNetworks.get(netId); 237 if (network == null) { 238 return; 239 } 240 mConnectivityManager.reportNetworkConnectivity(network, false); 241 showToast("Reporting Network " + netId); 242 } 243 244 /** 245 * Maps of NET_CAPABILITY_* and TRANSPORT_* to string representations. A network having these 246 * capabilities will have the following strings print on their list entry. 247 */ 248 private static final SparseArray<String> sTransportNames = new SparseArray<String>(); 249 private static final SparseArray<String> sCapabilityNames = new SparseArray<String>(); 250 static { sTransportNames.put(NetworkCapabilities.TRANSPORT_LOWPAN, "[LOWPAN]")251 sTransportNames.put(NetworkCapabilities.TRANSPORT_LOWPAN, "[LOWPAN]"); sTransportNames.put(NetworkCapabilities.TRANSPORT_WIFI_AWARE, "[WIFI-AWARE]")252 sTransportNames.put(NetworkCapabilities.TRANSPORT_WIFI_AWARE, "[WIFI-AWARE]"); sTransportNames.put(NetworkCapabilities.TRANSPORT_VPN, "[VPN]")253 sTransportNames.put(NetworkCapabilities.TRANSPORT_VPN, "[VPN]"); sTransportNames.put(NetworkCapabilities.TRANSPORT_ETHERNET, "[ETHERNET]")254 sTransportNames.put(NetworkCapabilities.TRANSPORT_ETHERNET, "[ETHERNET]"); sTransportNames.put(NetworkCapabilities.TRANSPORT_BLUETOOTH, "[BLUETOOTH]")255 sTransportNames.put(NetworkCapabilities.TRANSPORT_BLUETOOTH, "[BLUETOOTH]"); sTransportNames.put(NetworkCapabilities.TRANSPORT_WIFI, "[WIFI]")256 sTransportNames.put(NetworkCapabilities.TRANSPORT_WIFI, "[WIFI]"); sTransportNames.put(NetworkCapabilities.TRANSPORT_CELLULAR, "[CELLULAR]")257 sTransportNames.put(NetworkCapabilities.TRANSPORT_CELLULAR, "[CELLULAR]"); 258 sCapabilityNames.put(NetworkCapabilities.NET_CAPABILITY_CAPTIVE_PORTAL, "[CAPTIVE PORTAL]")259 sCapabilityNames.put(NetworkCapabilities.NET_CAPABILITY_CAPTIVE_PORTAL, "[CAPTIVE PORTAL]"); sCapabilityNames.put(NetworkCapabilities.NET_CAPABILITY_CBS, "[CBS]")260 sCapabilityNames.put(NetworkCapabilities.NET_CAPABILITY_CBS, "[CBS]"); sCapabilityNames.put(NetworkCapabilities.NET_CAPABILITY_DUN, "[DUN]")261 sCapabilityNames.put(NetworkCapabilities.NET_CAPABILITY_DUN, "[DUN]"); sCapabilityNames.put(NetworkCapabilities.NET_CAPABILITY_EIMS, "[EIMS]")262 sCapabilityNames.put(NetworkCapabilities.NET_CAPABILITY_EIMS, "[EIMS]"); sCapabilityNames.put(NetworkCapabilities.NET_CAPABILITY_FOREGROUND, "[FOREGROUND]")263 sCapabilityNames.put(NetworkCapabilities.NET_CAPABILITY_FOREGROUND, "[FOREGROUND]"); sCapabilityNames.put(NetworkCapabilities.NET_CAPABILITY_FOTA, "[FOTA]")264 sCapabilityNames.put(NetworkCapabilities.NET_CAPABILITY_FOTA, "[FOTA]"); sCapabilityNames.put(NetworkCapabilities.NET_CAPABILITY_IA, "[IA]")265 sCapabilityNames.put(NetworkCapabilities.NET_CAPABILITY_IA, "[IA]"); sCapabilityNames.put(NetworkCapabilities.NET_CAPABILITY_IMS, "[IMS]")266 sCapabilityNames.put(NetworkCapabilities.NET_CAPABILITY_IMS, "[IMS]"); sCapabilityNames.put(NetworkCapabilities.NET_CAPABILITY_INTERNET, "[INTERNET]")267 sCapabilityNames.put(NetworkCapabilities.NET_CAPABILITY_INTERNET, "[INTERNET]"); sCapabilityNames.put(NetworkCapabilities.NET_CAPABILITY_MMS, "[MMS]")268 sCapabilityNames.put(NetworkCapabilities.NET_CAPABILITY_MMS, "[MMS]"); sCapabilityNames.put(NetworkCapabilities.NET_CAPABILITY_NOT_CONGESTED, "[NOT CONGESTED]")269 sCapabilityNames.put(NetworkCapabilities.NET_CAPABILITY_NOT_CONGESTED, "[NOT CONGESTED]"); sCapabilityNames.put(NetworkCapabilities.NET_CAPABILITY_NOT_METERED, "[NOT METERED]")270 sCapabilityNames.put(NetworkCapabilities.NET_CAPABILITY_NOT_METERED, "[NOT METERED]"); sCapabilityNames.put(NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED, "[NOT RESTRICTED]")271 sCapabilityNames.put(NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED, "[NOT RESTRICTED]"); sCapabilityNames.put(NetworkCapabilities.NET_CAPABILITY_NOT_ROAMING, "[NOT ROAMING]")272 sCapabilityNames.put(NetworkCapabilities.NET_CAPABILITY_NOT_ROAMING, "[NOT ROAMING]"); sCapabilityNames.put(NetworkCapabilities.NET_CAPABILITY_NOT_SUSPENDED, "[NOT SUSPENDED]")273 sCapabilityNames.put(NetworkCapabilities.NET_CAPABILITY_NOT_SUSPENDED, "[NOT SUSPENDED]"); sCapabilityNames.put(NetworkCapabilities.NET_CAPABILITY_NOT_VPN, "[NOT VPN]")274 sCapabilityNames.put(NetworkCapabilities.NET_CAPABILITY_NOT_VPN, "[NOT VPN]"); sCapabilityNames.put(NetworkCapabilities.NET_CAPABILITY_RCS, "[RCS]")275 sCapabilityNames.put(NetworkCapabilities.NET_CAPABILITY_RCS, "[RCS]"); sCapabilityNames.put(NetworkCapabilities.NET_CAPABILITY_SUPL, "[SUPL]")276 sCapabilityNames.put(NetworkCapabilities.NET_CAPABILITY_SUPL, "[SUPL]"); sCapabilityNames.put(NetworkCapabilities.NET_CAPABILITY_TRUSTED, "[TRUSTED]")277 sCapabilityNames.put(NetworkCapabilities.NET_CAPABILITY_TRUSTED, "[TRUSTED]"); sCapabilityNames.put(NetworkCapabilities.NET_CAPABILITY_VALIDATED, "[VALIDATED]")278 sCapabilityNames.put(NetworkCapabilities.NET_CAPABILITY_VALIDATED, "[VALIDATED]"); sCapabilityNames.put(NetworkCapabilities.NET_CAPABILITY_WIFI_P2P, "[WIFI P2P]")279 sCapabilityNames.put(NetworkCapabilities.NET_CAPABILITY_WIFI_P2P, "[WIFI P2P]"); sCapabilityNames.put(NetworkCapabilities.NET_CAPABILITY_XCAP, "[XCAP]")280 sCapabilityNames.put(NetworkCapabilities.NET_CAPABILITY_XCAP, "[XCAP]"); 281 } 282 283 private static final SparseArray<String> sWifiStaStates = new SparseArray<>(); 284 static { sWifiStaStates.put(WifiManager.WIFI_STATE_DISABLING, "STA_DISABLING")285 sWifiStaStates.put(WifiManager.WIFI_STATE_DISABLING, "STA_DISABLING"); sWifiStaStates.put(WifiManager.WIFI_STATE_DISABLED, "STA_DISABLED")286 sWifiStaStates.put(WifiManager.WIFI_STATE_DISABLED, "STA_DISABLED"); sWifiStaStates.put(WifiManager.WIFI_STATE_ENABLING, "STA_ENABLING")287 sWifiStaStates.put(WifiManager.WIFI_STATE_ENABLING, "STA_ENABLING"); sWifiStaStates.put(WifiManager.WIFI_STATE_ENABLED, "STA_ENABLED")288 sWifiStaStates.put(WifiManager.WIFI_STATE_ENABLED, "STA_ENABLED"); sWifiStaStates.put(WifiManager.WIFI_STATE_UNKNOWN, "STA_UNKNOWN")289 sWifiStaStates.put(WifiManager.WIFI_STATE_UNKNOWN, "STA_UNKNOWN"); 290 } 291 292 private static final SparseArray<String> sWifiApStates = new SparseArray<>(); 293 static { sWifiApStates.put(WifiManager.WIFI_AP_STATE_DISABLING, "AP_DISABLING")294 sWifiApStates.put(WifiManager.WIFI_AP_STATE_DISABLING, "AP_DISABLING"); sWifiApStates.put(WifiManager.WIFI_AP_STATE_DISABLED, "AP_DISABLED")295 sWifiApStates.put(WifiManager.WIFI_AP_STATE_DISABLED, "AP_DISABLED"); sWifiApStates.put(WifiManager.WIFI_AP_STATE_ENABLING, "AP_ENABLING")296 sWifiApStates.put(WifiManager.WIFI_AP_STATE_ENABLING, "AP_ENABLING"); sWifiApStates.put(WifiManager.WIFI_AP_STATE_ENABLED, "AP_ENABLED")297 sWifiApStates.put(WifiManager.WIFI_AP_STATE_ENABLED, "AP_ENABLED"); sWifiApStates.put(WifiManager.WIFI_AP_STATE_FAILED, "AP_FAILED")298 sWifiApStates.put(WifiManager.WIFI_AP_STATE_FAILED, "AP_FAILED"); 299 } 300 301 /** 302 * Builds a string out of the possible transports that can be applied to a 303 * NetworkCapabilities object. 304 */ getTransportString(NetworkCapabilities nCaps)305 private String getTransportString(NetworkCapabilities nCaps) { 306 String transports = ""; 307 for (int transport : nCaps.getTransportTypes()) { 308 transports += sTransportNames.get(transport, ""); 309 } 310 return transports; 311 } 312 313 /** 314 * Builds a string out of the possible capabilities that can be applied to 315 * a NetworkCapabilities object. 316 */ getCapabilitiesString(NetworkCapabilities nCaps)317 private String getCapabilitiesString(NetworkCapabilities nCaps) { 318 String caps = ""; 319 for (int capability : nCaps.getCapabilities()) { 320 caps += sCapabilityNames.get(capability, ""); 321 } 322 return caps; 323 } 324 325 // Gets the string representation of a MAC address from a given NetworkInterface object getMacAddress(NetworkInterface ni)326 private String getMacAddress(NetworkInterface ni) { 327 if (ni == null) { 328 return "??:??:??:??:??:??"; 329 } 330 331 byte[] mac = null; 332 try { 333 mac = ni.getHardwareAddress(); 334 } catch (SocketException exception) { 335 Log.e(TAG, "SocketException -- Failed to get interface MAC address"); 336 return "??:??:??:??:??:??"; 337 } 338 339 if (mac == null) { 340 return "??:??:??:??:??:??"; 341 } 342 343 StringBuilder sb = new StringBuilder(18); 344 for (byte b : mac) { 345 if (sb.length() > 0) { 346 sb.append(':'); 347 } 348 sb.append(String.format("%02x", b)); 349 } 350 return sb.toString(); 351 } 352 353 /** 354 * Builds a NetworkItem object from a given Network object, aggregating info across Network, 355 * NetworkCapabilities, NetworkInfo, NetworkInterface, and LinkProperties objects and pass it 356 * all as a string for the UI to use 357 */ getNetworkItem(Network n)358 private NetworkItem getNetworkItem(Network n) { 359 360 // Get default network to assign the button text correctly 361 // NOTE: activeNetwork != ProcessDefault when you set one, active is tracking the default 362 // request regardless of your process's default 363 // Network defNetwork = mConnectivityManager.getActiveNetwork(); 364 Network defNetwork = mConnectivityManager.getBoundNetworkForProcess(); 365 366 // Used to get network state 367 NetworkInfo nInfo = mConnectivityManager.getNetworkInfo(n); 368 369 // Used to get transport type(s), capabilities 370 NetworkCapabilities nCaps = mConnectivityManager.getNetworkCapabilities(n); 371 372 // Properties of the actual physical link 373 LinkProperties nLink = mConnectivityManager.getLinkProperties(n); 374 375 // Object representing the actual interface 376 NetworkInterface nIface = null; 377 try { 378 nIface = NetworkInterface.getByName(nLink.getInterfaceName()); 379 } catch (SocketException exception) { 380 Log.e(TAG, "SocketException -- Failed to get interface info"); 381 } 382 383 // Pack NetworkItem with all values 384 NetworkItem ni = new NetworkItem(); 385 386 // Row key 387 ni.mNetId = n.getNetId(); 388 389 // LinkProperties/NetworkInterface 390 ni.mInterfaceName = "Interface: " + nLink.getInterfaceName() 391 + (nIface != null ? " (" + nIface.getName() + ")" : " ()"); 392 ni.mHwAddress = "HwAddress: " + getMacAddress(nIface); 393 ni.mIpAddresses = "IP Addresses: " + nLink.getLinkAddresses().toString(); 394 ni.mDnsAddresses = "DNS: " + nLink.getDnsServers().toString(); 395 ni.mDomains = "Domains: " + nLink.getDomains(); 396 ni.mRoutes = "Routes: " + nLink.getRoutes().toString(); 397 398 // NetworkInfo 399 ni.mType = "Type: " + nInfo.getTypeName() + " (" + nInfo.getSubtypeName() + ")"; 400 ni.mState = "State: " + nInfo.getState().name() + "/" + nInfo.getDetailedState().name(); 401 ni.mConnected = "Connected: " + (nInfo.isConnected() ? "Connected" : "Disconnected"); 402 ni.mAvailable = "Available: " + (nInfo.isAvailable() ? "Yes" : "No"); 403 ni.mRoaming = "Roaming: " + (nInfo.isRoaming() ? "Yes" : "No"); 404 405 // NetworkCapabilities 406 ni.mTransports = "Transports: " + getTransportString(nCaps); 407 ni.mCapabilities = "Capabilities: " + getCapabilitiesString(nCaps); 408 ni.mBandwidth = "Bandwidth (Down/Up): " + nCaps.getLinkDownstreamBandwidthKbps() 409 + " Kbps/" + nCaps.getLinkUpstreamBandwidthKbps() + " Kbps"; 410 411 // Other inferred values 412 ni.mDefault = sameNetworkId(n, defNetwork); 413 ni.mRequested = (mNetworkCallbacks.get(n.getNetId()) != null); 414 415 return ni; 416 } 417 418 // Refresh the networks content and prompt the user that we did it refreshNetworksAndPrompt()419 private void refreshNetworksAndPrompt() { 420 refreshNetworks(); 421 showToast("Refreshed Networks (" + mNetworkItems.length + ")"); 422 } 423 424 /** 425 * Gets the current set of networks from the connectivity manager and 1) stores the network 426 * objects 2) builds NetworkItem objects for the view to render and 3) If a network we were 427 * tracking disappears then it kills its callback. 428 */ refreshNetworks()429 private void refreshNetworks() { 430 Log.i(TAG, "refreshNetworks()"); 431 Network[] networks = mConnectivityManager.getAllNetworks(); 432 mNetworkItems = new NetworkItem[networks.length]; 433 mNetworks.clear(); 434 435 // Add each network to the network info set, turning each field to a string 436 for (int i = 0; i < networks.length; i++) { 437 mNetworkItems[i] = getNetworkItem(networks[i]); 438 mNetworks.put(networks[i].getNetId(), networks[i]); 439 } 440 441 // Check for callbacks that belong to networks that don't exist anymore 442 for (int i = 0; i < mNetworkCallbacks.size(); i++) { 443 int key = mNetworkCallbacks.keyAt(i); 444 if (mNetworks.get(key) == null) { 445 mNetworkCallbacks.remove(key); 446 } 447 } 448 449 // Update the view 450 mNetworksAdapter.refreshNetworks(mNetworkItems); 451 } 452 453 @Override onCreate(Bundle savedInstanceState)454 public void onCreate(Bundle savedInstanceState) { 455 super.onCreate(savedInstanceState); 456 457 Context ctx = getContext(); 458 mConnectivityManager = ctx.getSystemService(ConnectivityManager.class); 459 mWifiManager = ctx.getSystemService(WifiManager.class); 460 mLocationManager = ctx.getSystemService(LocationManager.class); 461 462 mConnectivityManager.addDefaultNetworkActiveListener(() -> refreshNetworks()); 463 } 464 465 @Nullable 466 @Override onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState)467 public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, 468 @Nullable Bundle savedInstanceState) { 469 View view = inflater.inflate(R.layout.connectivity_fragment, container, false); 470 471 ViewPager pager = view.findViewById(R.id.connectivity_pager); 472 pager.setAdapter(new SimplePagerAdapter(pager)); 473 474 // Create the ListView of all networks 475 ListView networksView = view.findViewById(R.id.networks); 476 mNetworksAdapter = new NetworkListAdapter(getContext(), mNetworkItems, this); 477 networksView.setAdapter(mNetworksAdapter); 478 479 // Find all networks ListView refresher and set the refresh callback 480 mNetworkListRefresher = (SwipeRefreshLayout) view.findViewById(R.id.refreshNetworksList); 481 mNetworkListRefresher.setOnRefreshListener(() -> { 482 refreshNetworksAndPrompt(); 483 mNetworkListRefresher.setRefreshing(false); 484 }); 485 486 view.findViewById(R.id.startWifi).setOnClickListener(v -> setWifiEnabled(true)); 487 view.findViewById(R.id.stopWifi).setOnClickListener(v -> setWifiEnabled(false)); 488 view.findViewById(R.id.startTethering).setOnClickListener(v -> startTethering()); 489 view.findViewById(R.id.stopTethering).setOnClickListener(v -> stopTethering()); 490 view.findViewById(R.id.startLocalOnly).setOnClickListener(v -> startLocalOnly()); 491 view.findViewById(R.id.stopLocalOnly).setOnClickListener(v -> stopLocalOnly()); 492 mWifiStatusPolled = (TextView) view.findViewById(R.id.wifiStatusPolled); 493 mTetheringStatus = (TextView) view.findViewById(R.id.tetheringStatus); 494 mTetheringStatusPolled = (TextView) view.findViewById(R.id.tetheringStatusPolled); 495 mLocalOnlyStatus = (TextView) view.findViewById(R.id.localOnlyStatus); 496 497 view.findViewById(R.id.networkEnableWifiIntent).setOnClickListener(v -> enableWifiIntent()); 498 view.findViewById(R.id.networkDisableWifiIntent) 499 .setOnClickListener(v -> disableWifiIntent()); 500 view.findViewById(R.id.networkEnableBluetoothIntent) 501 .setOnClickListener(v -> enableBluetoothIntent()); 502 view.findViewById(R.id.networkDisableBluetoothIntent) 503 .setOnClickListener(v -> disableBluetoothIntent()); 504 view.findViewById(R.id.networkDiscoverableBluetoothIntent) 505 .setOnClickListener(v -> discoverableBluetoothIntent()); 506 507 return view; 508 } 509 enableWifiIntent()510 private void enableWifiIntent() { 511 Intent enableWifi = new Intent(WifiManager.ACTION_REQUEST_ENABLE); 512 enableWifi.putExtra(Intent.EXTRA_PACKAGE_NAME, getContext().getPackageName()); 513 startActivity(enableWifi); 514 } 515 disableWifiIntent()516 private void disableWifiIntent() { 517 Intent disableWifi = new Intent(WifiManager.ACTION_REQUEST_DISABLE); 518 disableWifi.putExtra(Intent.EXTRA_PACKAGE_NAME, getContext().getPackageName()); 519 startActivity(disableWifi); 520 } 521 enableBluetoothIntent()522 private void enableBluetoothIntent() { 523 Intent enableBluetooth = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE); 524 enableBluetooth.putExtra(Intent.EXTRA_PACKAGE_NAME, getContext().getPackageName()); 525 startActivity(enableBluetooth); 526 } 527 disableBluetoothIntent()528 private void disableBluetoothIntent() { 529 Intent disableBluetooth = new Intent(BluetoothAdapter.ACTION_REQUEST_DISABLE); 530 disableBluetooth.putExtra(Intent.EXTRA_PACKAGE_NAME, getContext().getPackageName()); 531 startActivity(disableBluetooth); 532 } 533 discoverableBluetoothIntent()534 private void discoverableBluetoothIntent() { 535 Intent discoverableBluetooth = new Intent(BluetoothAdapter.ACTION_REQUEST_DISCOVERABLE); 536 discoverableBluetooth.putExtra(Intent.EXTRA_PACKAGE_NAME, getContext().getPackageName()); 537 startActivity(discoverableBluetooth); 538 } 539 540 @Override onResume()541 public void onResume() { 542 super.onResume(); 543 refreshNetworks(); 544 mWifiUpdater = new Timer(); 545 mWifiUpdater.scheduleAtFixedRate(new TimerTask() { 546 547 @Override 548 public void run() { 549 updateApState(); 550 } 551 }, 0, 500); 552 } 553 554 @Override onPause()555 public void onPause() { 556 super.onPause(); 557 releaseAllNetworks(); 558 mWifiUpdater.cancel(); 559 mWifiUpdater = null; 560 } 561 updateApState()562 private void updateApState() { 563 int apState = mWifiManager.getWifiApState(); 564 String apStateTmp = sWifiApStates.get(apState, "?"); 565 final String staStateStr = sWifiStaStates.get(mWifiManager.getWifiState(), "?"); 566 567 WifiConfiguration config = mWifiManager.getWifiApConfiguration(); 568 if (config != null && config.SSID != null && apState == WifiManager.WIFI_AP_STATE_ENABLED) { 569 apStateTmp += " (" + config.SSID + "/" + config.preSharedKey + ")"; 570 } 571 572 final String apStateStr = apStateTmp; 573 mTetheringStatusPolled.post(() -> { 574 mTetheringStatusPolled.setText(apStateStr); 575 mWifiStatusPolled.setText(staStateStr); 576 }); 577 } 578 setTetheringStatus(String status)579 private void setTetheringStatus(String status) { 580 mTetheringStatus.post(() -> mTetheringStatus.setText(status)); 581 } 582 setLocalOnlyStatus(String status)583 private void setLocalOnlyStatus(String status) { 584 mLocalOnlyStatus.post(() -> mLocalOnlyStatus.setText(status)); 585 } 586 showToast(String text)587 public void showToast(String text) { 588 Toast.makeText(getContext(), text, Toast.LENGTH_SHORT).show(); 589 } 590 sameNetworkId(Network net1, Network net2)591 private static boolean sameNetworkId(Network net1, Network net2) { 592 return net1 != null && net2 != null && net1.getNetId() == net2.getNetId(); 593 } 594 setWifiEnabled(boolean enabled)595 private void setWifiEnabled(boolean enabled) { 596 mWifiManager.setWifiEnabled(enabled); 597 } 598 startTethering()599 private void startTethering() { 600 setTetheringStatus("starting..."); 601 602 ConnectivityManager.OnStartTetheringCallback cb = 603 new ConnectivityManager.OnStartTetheringCallback() { 604 public void onTetheringStarted() { 605 setTetheringStatus("started"); 606 } 607 608 public void onTetheringFailed() { 609 setTetheringStatus("failed"); 610 } 611 }; 612 613 mConnectivityManager.startTethering(ConnectivityManager.TETHERING_WIFI, false, cb); 614 } 615 stopTethering()616 private void stopTethering() { 617 setTetheringStatus("stopping..."); 618 mConnectivityManager.stopTethering(ConnectivityManager.TETHERING_WIFI); 619 setTetheringStatus("stopped"); 620 } 621 622 private WifiManager.LocalOnlyHotspotReservation mLocalOnlyReservation; 623 startLocalOnly()624 private void startLocalOnly() { 625 setLocalOnlyStatus("starting..."); 626 627 UserHandle user = Process.myUserHandle(); 628 if (!mLocationManager.isLocationEnabledForUser(user)) { 629 setLocalOnlyStatus("enabling location..."); 630 mLocationManager.setLocationEnabledForUser(true, user); 631 setLocalOnlyStatus("location enabled; starting..."); 632 } 633 634 WifiManager.LocalOnlyHotspotCallback cb = new WifiManager.LocalOnlyHotspotCallback() { 635 public void onStarted(WifiManager.LocalOnlyHotspotReservation reservation) { 636 mLocalOnlyReservation = reservation; 637 WifiConfiguration config = reservation.getWifiConfiguration(); 638 setLocalOnlyStatus("started (" 639 + config.SSID + "/" + config.preSharedKey + ")"); 640 }; 641 642 public void onStopped() { 643 setLocalOnlyStatus("stopped"); 644 }; 645 646 public void onFailed(int reason) { 647 setLocalOnlyStatus("failed " + reason); 648 }; 649 }; 650 651 try { 652 mWifiManager.startLocalOnlyHotspot(cb, null); 653 } catch (IllegalStateException ex) { 654 setLocalOnlyStatus(ex.getMessage()); 655 } 656 } 657 stopLocalOnly()658 private void stopLocalOnly() { 659 setLocalOnlyStatus("stopping..."); 660 661 WifiManager.LocalOnlyHotspotReservation reservation = mLocalOnlyReservation; 662 mLocalOnlyReservation = null; 663 664 if (reservation == null) { 665 setLocalOnlyStatus("no reservation"); 666 return; 667 } 668 669 reservation.close(); 670 setLocalOnlyStatus("stopped"); 671 } 672 } 673