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