1 /*
2  * Copyright (C) 2014 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.tv.settings.connectivity;
18 
19 import android.annotation.SuppressLint;
20 import android.content.BroadcastReceiver;
21 import android.content.Context;
22 import android.content.Intent;
23 import android.content.IntentFilter;
24 import android.net.ConnectivityManager;
25 import android.net.EthernetManager;
26 import android.net.LinkAddress;
27 import android.net.LinkProperties;
28 import android.net.Network;
29 import android.net.NetworkInfo;
30 import android.net.wifi.WifiConfiguration;
31 import android.net.wifi.WifiInfo;
32 import android.net.wifi.WifiManager;
33 import android.telephony.PhoneStateListener;
34 import android.telephony.SignalStrength;
35 import android.telephony.TelephonyManager;
36 import android.text.TextUtils;
37 import android.util.Log;
38 
39 import androidx.annotation.UiThread;
40 
41 import com.android.settingslib.core.lifecycle.Lifecycle;
42 import com.android.settingslib.core.lifecycle.LifecycleObserver;
43 import com.android.settingslib.core.lifecycle.events.OnStart;
44 import com.android.settingslib.core.lifecycle.events.OnStop;
45 import com.android.settingslib.wifi.AccessPoint;
46 import com.android.settingslib.wifi.WifiTracker;
47 
48 import java.util.ArrayList;
49 import java.util.List;
50 
51 /**
52  * Listens for changes to the current connectivity status.
53  */
54 public class ConnectivityListener implements WifiTracker.WifiListener, LifecycleObserver, OnStart,
55         OnStop {
56 
57     private static final String TAG = "ConnectivityListener";
58 
59     private final Context mContext;
60     private final Listener mListener;
61     private boolean mStarted;
62 
63     private WifiTracker mWifiTracker;
64 
65     private final ConnectivityManager mConnectivityManager;
66     private final WifiManager mWifiManager;
67     private final EthernetManager mEthernetManager;
68     private WifiNetworkListener mWifiListener;
69     private final BroadcastReceiver mNetworkReceiver = new BroadcastReceiver() {
70         @Override
71         public void onReceive(Context context, Intent intent) {
72             updateConnectivityStatus();
73             if (mListener != null) {
74                 mListener.onConnectivityChange();
75             }
76         }
77     };
78     private final PhoneStateListener mPhoneStateListener = new PhoneStateListener() {
79         @Override
80         public void onSignalStrengthsChanged(SignalStrength signalStrength) {
81             mCellSignalStrength = signalStrength;
82             mListener.onConnectivityChange();
83         }
84     };
85 
86     private SignalStrength mCellSignalStrength;
87     private int mNetworkType;
88     private String mWifiSsid;
89     private int mWifiSignalStrength;
90 
91     /**
92      * @deprecated use the constructor that provides a {@link Lifecycle} instead
93      */
94     @Deprecated
ConnectivityListener(Context context, Listener listener)95     public ConnectivityListener(Context context, Listener listener) {
96         this(context, listener, null);
97     }
98 
ConnectivityListener(Context context, Listener listener, Lifecycle lifecycle)99     public ConnectivityListener(Context context, Listener listener, Lifecycle lifecycle) {
100         mContext = context;
101         mConnectivityManager = (ConnectivityManager) mContext.getSystemService(
102                 Context.CONNECTIVITY_SERVICE);
103         mWifiManager = mContext.getSystemService(WifiManager.class);
104         mEthernetManager = mContext.getSystemService(EthernetManager.class);
105         mListener = listener;
106         if (mWifiManager != null) {
107             if (lifecycle != null) {
108                 lifecycle.addObserver(this);
109                 mWifiTracker = new WifiTracker(context, this, lifecycle, true, true);
110             } else {
111                 mWifiTracker = new WifiTracker(context, this, true, true);
112             }
113         }
114         updateConnectivityStatus();
115     }
116 
117     /**
118      * Starts {@link ConnectivityListener}.
119      * This should be called only from main thread.
120      * @deprecated not needed when a {@link Lifecycle} is provided
121      */
122     @UiThread
123     @Deprecated
start()124     public void start() {
125         if (!mStarted && mWifiTracker != null) {
126             mWifiTracker.onStart();
127         }
128         onStart();
129     }
130 
131     @Override
onStart()132     public void onStart() {
133         if (!mStarted) {
134             mStarted = true;
135             updateConnectivityStatus();
136             IntentFilter networkIntentFilter = new IntentFilter();
137             networkIntentFilter.addAction(WifiManager.NETWORK_STATE_CHANGED_ACTION);
138             networkIntentFilter.addAction(WifiManager.RSSI_CHANGED_ACTION);
139             networkIntentFilter.addAction(WifiManager.WIFI_STATE_CHANGED_ACTION);
140             networkIntentFilter.addAction(ConnectivityManager.CONNECTIVITY_ACTION);
141 
142             mContext.registerReceiver(mNetworkReceiver, networkIntentFilter);
143             final TelephonyManager telephonyManager = mContext
144                     .getSystemService(TelephonyManager.class);
145             if (telephonyManager != null) {
146                 telephonyManager.listen(mPhoneStateListener,
147                         PhoneStateListener.LISTEN_SIGNAL_STRENGTHS);
148             }
149         }
150     }
151 
152     /**
153      * Stops {@link ConnectivityListener}.
154      * This should be called only from main thread.
155      * @deprecated not needed when a {@link Lifecycle} is provided
156      */
157     @UiThread
158     @Deprecated
stop()159     public void stop() {
160         if (mStarted && mWifiTracker != null) {
161             mWifiTracker.onStop();
162         }
163         onStop();
164     }
165 
166     @Override
onStop()167     public void onStop() {
168         if (mStarted) {
169             mStarted = false;
170             mContext.unregisterReceiver(mNetworkReceiver);
171             mWifiListener = null;
172             final TelephonyManager telephonyManager = mContext
173                     .getSystemService(TelephonyManager.class);
174             if (telephonyManager != null) {
175                 telephonyManager.listen(mPhoneStateListener, PhoneStateListener.LISTEN_NONE);
176             }
177         }
178     }
179 
180     /**
181      * Causes the background thread to quit.
182      * @deprecated not needed when a {@link Lifecycle} is provided
183      */
184     @Deprecated
destroy()185     public void destroy() {
186         if (mWifiTracker != null) {
187             mWifiTracker.onDestroy();
188         }
189     }
190 
setWifiListener(WifiNetworkListener wifiListener)191     public void setWifiListener(WifiNetworkListener wifiListener) {
192         mWifiListener = wifiListener;
193     }
194 
getWifiIpAddress()195     public String getWifiIpAddress() {
196         if (isWifiConnected()) {
197             Network network = mWifiManager.getCurrentNetwork();
198             return formatIpAddresses(network);
199         } else {
200             return "";
201         }
202     }
203 
204     /**
205      * Return the MAC address of the currently connected Wifi AP.
206      */
207     @SuppressLint("HardwareIds")
getWifiMacAddress(AccessPoint ap)208     public String getWifiMacAddress(AccessPoint ap) {
209         if (isWifiConnected() && mWifiManager.getConnectionInfo() != null) {
210             return mWifiManager.getConnectionInfo().getMacAddress();
211         }
212         if (ap != null) {
213             WifiConfiguration wifiConfig = ap.getConfig();
214             if (wifiConfig != null
215                     && wifiConfig.macRandomizationSetting
216                             == WifiConfiguration.RANDOMIZATION_PERSISTENT) {
217                 return wifiConfig.getRandomizedMacAddress().toString();
218             }
219         }
220 
221         // return device MAC address
222         final String[] macAddresses = mWifiManager.getFactoryMacAddresses();
223         if (macAddresses != null && macAddresses.length > 0) {
224             return macAddresses[0];
225         }
226 
227         Log.e(TAG, "Unable to get MAC address");
228         return "";
229     }
230 
231     /** Return whether the connected Wifi supports MAC address randomization. */
isMacAddressRandomizationSupported()232     public boolean isMacAddressRandomizationSupported() {
233         return mWifiManager.isConnectedMacRandomizationSupported();
234     }
235 
236     /** Return whether the MAC address of the currently connected Wifi AP is randomized. */
getWifiMacRandomizationSetting(AccessPoint ap)237     public int getWifiMacRandomizationSetting(AccessPoint ap) {
238         if (ap == null || ap.getConfig() == null) {
239             return WifiConfiguration.RANDOMIZATION_NONE;
240         }
241         return ap.getConfig().macRandomizationSetting;
242     }
243 
244     /** Apply the setting of whether to use MAC address randimization. */
applyMacRandomizationSetting(AccessPoint ap, boolean enable)245     public void applyMacRandomizationSetting(AccessPoint ap, boolean enable) {
246         if (ap != null && ap.getConfig() != null) {
247             ap.getConfig().macRandomizationSetting = enable
248                     ? WifiConfiguration.RANDOMIZATION_PERSISTENT
249                     : WifiConfiguration.RANDOMIZATION_NONE;
250             mWifiManager.updateNetwork(ap.getConfig());
251             // To activate changing, we need to reconnect network. WiFi will auto connect to
252             // current network after disconnect(). Only needed when this is connected network.
253             final WifiInfo wifiInfo = mWifiManager.getConnectionInfo();
254             if (wifiInfo != null && wifiInfo.getNetworkId() == ap.getConfig().networkId) {
255                 mWifiManager.disconnect();
256             }
257         }
258     }
259 
isEthernetConnected()260     public boolean isEthernetConnected() {
261         return mNetworkType == ConnectivityManager.TYPE_ETHERNET;
262     }
263 
isWifiConnected()264     public boolean isWifiConnected() {
265         if (mNetworkType == ConnectivityManager.TYPE_WIFI) {
266             return true;
267         } else {
268             if (mWifiManager != null) {
269                 WifiInfo connectionInfo = mWifiManager.getConnectionInfo();
270                 return connectionInfo.getNetworkId() != -1;
271             }
272         }
273         return false;
274     }
275 
isCellConnected()276     public boolean isCellConnected() {
277         return mNetworkType == ConnectivityManager.TYPE_MOBILE;
278     }
279 
280     /**
281      * Return whether Ethernet port is available.
282      */
isEthernetAvailable()283     public boolean isEthernetAvailable() {
284         return mConnectivityManager.isNetworkSupported(ConnectivityManager.TYPE_ETHERNET)
285                 && mEthernetManager.getAvailableInterfaces().length > 0;
286     }
287 
getFirstEthernet()288     private Network getFirstEthernet() {
289         final Network[] networks = mConnectivityManager.getAllNetworks();
290         for (final Network network : networks) {
291             NetworkInfo networkInfo = mConnectivityManager.getNetworkInfo(network);
292             if (networkInfo != null && networkInfo.getType() == ConnectivityManager.TYPE_ETHERNET) {
293                 return network;
294             }
295         }
296         return null;
297     }
298 
formatIpAddresses(Network network)299     private String formatIpAddresses(Network network) {
300         final LinkProperties linkProperties = mConnectivityManager.getLinkProperties(network);
301         if (linkProperties == null) {
302             return null;
303         }
304         final StringBuilder sb = new StringBuilder();
305         boolean gotAddress = false;
306         for (LinkAddress linkAddress : linkProperties.getLinkAddresses()) {
307             if (gotAddress) {
308                 sb.append("\n");
309             }
310             sb.append(linkAddress.getAddress().getHostAddress());
311             gotAddress = true;
312         }
313         if (gotAddress) {
314             return sb.toString();
315         } else {
316             return null;
317         }
318     }
319 
320     /**
321      * Returns the formatted IP addresses of the Ethernet connection or null
322      * if none available.
323      */
getEthernetIpAddress()324     public String getEthernetIpAddress() {
325         final Network network = getFirstEthernet();
326         if (network == null) {
327             return null;
328         }
329         return formatIpAddresses(network);
330     }
331 
getWifiSignalStrength(int maxLevel)332     public int getWifiSignalStrength(int maxLevel) {
333         if (mWifiManager != null) {
334             WifiInfo wifiInfo = mWifiManager.getConnectionInfo();
335             return WifiManager.calculateSignalLevel(wifiInfo.getRssi(), maxLevel);
336         }
337         return 0;
338     }
339 
getCellSignalStrength()340     public int getCellSignalStrength() {
341         if (isCellConnected() && mCellSignalStrength != null) {
342             return mCellSignalStrength.getLevel();
343         } else {
344             return 0;
345         }
346     }
347 
348     /**
349      * Return a list of wifi networks. Ensure that if a wifi network is connected that it appears
350      * as the first item on the list.
351      */
getAvailableNetworks()352     public List<AccessPoint> getAvailableNetworks() {
353         return mWifiTracker == null ? new ArrayList<>() : mWifiTracker.getAccessPoints();
354     }
355 
isWifiEnabledOrEnabling()356     public boolean isWifiEnabledOrEnabling() {
357         return mWifiManager != null
358                 && (mWifiManager.getWifiState() == WifiManager.WIFI_STATE_ENABLED
359                 || mWifiManager.getWifiState() == WifiManager.WIFI_STATE_ENABLING);
360     }
361 
setWifiEnabled(boolean enable)362     public void setWifiEnabled(boolean enable) {
363         if (mWifiManager != null) {
364             mWifiManager.setWifiEnabled(enable);
365         }
366     }
367 
updateConnectivityStatus()368     private void updateConnectivityStatus() {
369         NetworkInfo networkInfo = mConnectivityManager.getActiveNetworkInfo();
370         if (networkInfo == null) {
371             mNetworkType = ConnectivityManager.TYPE_NONE;
372         } else {
373             switch (networkInfo.getType()) {
374                 case ConnectivityManager.TYPE_WIFI: {
375 
376                     if (mWifiManager == null) {
377                         break;
378                     }
379                     // Determine if this is
380                     // an open or secure wifi connection.
381                     mNetworkType = ConnectivityManager.TYPE_WIFI;
382 
383                     String ssid = getSsid();
384                     if (!TextUtils.equals(mWifiSsid, ssid)) {
385                         mWifiSsid = ssid;
386                     }
387 
388                     WifiInfo wifiInfo = mWifiManager.getConnectionInfo();
389                     // Calculate the signal strength.
390                     int signalStrength;
391                     if (wifiInfo != null) {
392                         // Calculate the signal strength between 0 and 3.
393                         signalStrength = WifiManager.calculateSignalLevel(wifiInfo.getRssi(), 4);
394                     } else {
395                         signalStrength = 0;
396                     }
397                     if (mWifiSignalStrength != signalStrength) {
398                         mWifiSignalStrength = signalStrength;
399                     }
400                     break;
401                 }
402 
403                 case ConnectivityManager.TYPE_ETHERNET:
404                     mNetworkType = ConnectivityManager.TYPE_ETHERNET;
405                     break;
406 
407                 case ConnectivityManager.TYPE_MOBILE:
408                     mNetworkType = ConnectivityManager.TYPE_MOBILE;
409                     break;
410 
411                 default:
412                     mNetworkType = ConnectivityManager.TYPE_NONE;
413                     break;
414             }
415         }
416     }
417 
418     @Override
onWifiStateChanged(int state)419     public void onWifiStateChanged(int state) {
420         updateConnectivityStatus();
421         if (mListener != null) {
422             mListener.onConnectivityChange();
423         }
424     }
425 
426     @Override
onConnectedChanged()427     public void onConnectedChanged() {
428         updateConnectivityStatus();
429         if (mListener != null) {
430             mListener.onConnectivityChange();
431         }
432     }
433 
434     @Override
onAccessPointsChanged()435     public void onAccessPointsChanged() {
436         if (mWifiListener != null) {
437             mWifiListener.onWifiListChanged();
438         }
439     }
440 
441     public interface Listener {
onConnectivityChange()442         void onConnectivityChange();
443     }
444 
445     public interface WifiNetworkListener {
onWifiListChanged()446         void onWifiListChanged();
447     }
448 
449     /**
450      * Get the SSID of current connected network.
451      * @return SSID
452      */
getSsid()453     public String getSsid() {
454         if (mWifiManager == null) {
455             return null;
456         }
457         WifiInfo wifiInfo = mWifiManager.getConnectionInfo();
458         // Find the SSID of network.
459         String ssid = null;
460         if (wifiInfo != null) {
461             ssid = wifiInfo.getSSID();
462             if (ssid != null) {
463                 ssid = WifiInfo.sanitizeSsid(ssid);
464             }
465         }
466         return ssid;
467     }
468 }
469