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