OSDN Git Service

[automerger] [RESTRICT AUTOMERGE] Make ScreenPinningSettings behaviour consistent...
[android-x86/packages-apps-Settings.git] / src / com / android / settings / wifi / details / WifiDetailPreferenceController.java
1 /*
2  * Copyright (C) 2017 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 package com.android.settings.wifi.details;
17
18 import static android.net.NetworkCapabilities.NET_CAPABILITY_CAPTIVE_PORTAL;
19 import static android.net.NetworkCapabilities.NET_CAPABILITY_VALIDATED;
20 import static android.net.NetworkCapabilities.TRANSPORT_WIFI;
21
22 import android.app.Fragment;
23 import android.content.BroadcastReceiver;
24 import android.content.Context;
25 import android.content.Intent;
26 import android.content.IntentFilter;
27 import android.graphics.drawable.Drawable;
28 import android.net.ConnectivityManager;
29 import android.net.ConnectivityManager.NetworkCallback;
30 import android.net.LinkAddress;
31 import android.net.LinkProperties;
32 import android.net.Network;
33 import android.net.NetworkBadging;
34 import android.net.NetworkCapabilities;
35 import android.net.NetworkInfo;
36 import android.net.NetworkRequest;
37 import android.net.NetworkUtils;
38 import android.net.RouteInfo;
39 import android.net.wifi.WifiConfiguration;
40 import android.net.wifi.WifiInfo;
41 import android.net.wifi.WifiManager;
42 import android.os.Handler;
43 import android.support.v7.preference.Preference;
44 import android.support.v7.preference.PreferenceCategory;
45 import android.support.v7.preference.PreferenceScreen;
46 import android.text.TextUtils;
47 import android.util.Log;
48 import android.view.View;
49 import android.widget.Button;
50 import android.widget.ImageView;
51
52 import com.android.internal.annotations.VisibleForTesting;
53 import com.android.internal.logging.nano.MetricsProto;
54 import com.android.settings.R;
55 import com.android.settings.Utils;
56 import com.android.settings.applications.LayoutPreference;
57 import com.android.settings.core.PreferenceController;
58 import com.android.settings.core.instrumentation.MetricsFeatureProvider;
59 import com.android.settings.vpn2.ConnectivityManagerWrapper;
60 import com.android.settings.widget.EntityHeaderController;
61 import com.android.settings.wifi.WifiDetailPreference;
62 import com.android.settingslib.core.lifecycle.Lifecycle;
63 import com.android.settingslib.core.lifecycle.LifecycleObserver;
64 import com.android.settingslib.core.lifecycle.events.OnPause;
65 import com.android.settingslib.core.lifecycle.events.OnResume;
66 import com.android.settingslib.wifi.AccessPoint;
67
68 import java.net.Inet4Address;
69 import java.net.Inet6Address;
70 import java.net.InetAddress;
71 import java.net.UnknownHostException;
72 import java.util.StringJoiner;
73 import java.util.stream.Collectors;
74
75 /**
76  * Controller for logic pertaining to displaying Wifi information for the
77  * {@link WifiNetworkDetailsFragment}.
78  */
79 public class WifiDetailPreferenceController extends PreferenceController implements
80         LifecycleObserver, OnPause, OnResume {
81     private static final String TAG = "WifiDetailsPrefCtrl";
82     private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
83
84     @VisibleForTesting
85     static final String KEY_HEADER = "connection_header";
86     @VisibleForTesting
87     static final String KEY_BUTTONS_PREF = "buttons";
88     @VisibleForTesting
89     static final String KEY_SIGNAL_STRENGTH_PREF = "signal_strength";
90     @VisibleForTesting
91     static final String KEY_LINK_SPEED = "link_speed";
92     @VisibleForTesting
93     static final String KEY_FREQUENCY_PREF = "frequency";
94     @VisibleForTesting
95     static final String KEY_SECURITY_PREF = "security";
96     @VisibleForTesting
97     static final String KEY_MAC_ADDRESS_PREF = "mac_address";
98     @VisibleForTesting
99     static final String KEY_IP_ADDRESS_PREF = "ip_address";
100     @VisibleForTesting
101     static final String KEY_GATEWAY_PREF = "gateway";
102     @VisibleForTesting
103     static final String KEY_SUBNET_MASK_PREF = "subnet_mask";
104     @VisibleForTesting
105     static final String KEY_DNS_PREF = "dns";
106     @VisibleForTesting
107     static final String KEY_IPV6_CATEGORY = "ipv6_category";
108     @VisibleForTesting
109     static final String KEY_IPV6_ADDRESSES_PREF = "ipv6_addresses";
110
111     private AccessPoint mAccessPoint;
112     private final ConnectivityManagerWrapper mConnectivityManagerWrapper;
113     private final ConnectivityManager mConnectivityManager;
114     private final Fragment mFragment;
115     private final Handler mHandler;
116     private LinkProperties mLinkProperties;
117     private Network mNetwork;
118     private NetworkInfo mNetworkInfo;
119     private NetworkCapabilities mNetworkCapabilities;
120     private int mRssi;
121     private String[] mSignalStr;
122     private final WifiConfiguration mWifiConfig;
123     private WifiInfo mWifiInfo;
124     private final WifiManager mWifiManager;
125     private final MetricsFeatureProvider mMetricsFeatureProvider;
126
127     // UI elements - in order of appearance
128     private LayoutPreference mButtonsPref;
129     private EntityHeaderController mEntityHeaderController;
130     private Button mForgetButton;
131     private Button mSignInButton;
132     private WifiDetailPreference mSignalStrengthPref;
133     private WifiDetailPreference mLinkSpeedPref;
134     private WifiDetailPreference mFrequencyPref;
135     private WifiDetailPreference mSecurityPref;
136     private WifiDetailPreference mMacAddressPref;
137     private WifiDetailPreference mIpAddressPref;
138     private WifiDetailPreference mGatewayPref;
139     private WifiDetailPreference mSubnetPref;
140     private WifiDetailPreference mDnsPref;
141     private PreferenceCategory mIpv6Category;
142     private Preference mIpv6AddressPref;
143
144     private final IntentFilter mFilter;
145     private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
146         @Override
147         public void onReceive(Context context, Intent intent) {
148             switch (intent.getAction()) {
149                 case WifiManager.NETWORK_STATE_CHANGED_ACTION:
150                 case WifiManager.RSSI_CHANGED_ACTION:
151                     updateInfo();
152             }
153         }
154     };
155
156     private final NetworkRequest mNetworkRequest = new NetworkRequest.Builder()
157             .clearCapabilities().addTransportType(TRANSPORT_WIFI).build();
158
159     // Must be run on the UI thread since it directly manipulates UI state.
160     private final NetworkCallback mNetworkCallback = new NetworkCallback() {
161         @Override
162         public void onLinkPropertiesChanged(Network network, LinkProperties lp) {
163             if (network.equals(mNetwork) && !lp.equals(mLinkProperties)) {
164                 mLinkProperties = lp;
165                 updateIpLayerInfo();
166             }
167         }
168
169         private boolean hasCapabilityChanged(NetworkCapabilities nc, int cap) {
170             // If this is the first time we get NetworkCapabilities, report that something changed.
171             if (mNetworkCapabilities == null) return true;
172
173             // nc can never be null, see ConnectivityService#callCallbackForRequest.
174             return mNetworkCapabilities.hasCapability(cap) != nc.hasCapability(cap);
175         }
176
177         @Override
178         public void onCapabilitiesChanged(Network network, NetworkCapabilities nc) {
179             // If the network just validated or lost Internet access, refresh network state.
180             // Don't do this on every NetworkCapabilities change because refreshNetworkState
181             // sends IPCs to the system server from the UI thread, which can cause jank.
182             if (network.equals(mNetwork) && !nc.equals(mNetworkCapabilities)) {
183                 if (hasCapabilityChanged(nc, NET_CAPABILITY_VALIDATED) ||
184                         hasCapabilityChanged(nc, NET_CAPABILITY_CAPTIVE_PORTAL)) {
185                     refreshNetworkState();
186                 }
187                 mNetworkCapabilities = nc;
188                 updateIpLayerInfo();
189             }
190         }
191
192         @Override
193         public void onLost(Network network) {
194             if (network.equals(mNetwork)) {
195                 exitActivity();
196             }
197         }
198     };
199
200     public WifiDetailPreferenceController(
201             AccessPoint accessPoint,
202             ConnectivityManagerWrapper connectivityManagerWrapper,
203             Context context,
204             Fragment fragment,
205             Handler handler,
206             Lifecycle lifecycle,
207             WifiManager wifiManager,
208             MetricsFeatureProvider metricsFeatureProvider) {
209         super(context);
210
211         mAccessPoint = accessPoint;
212         mConnectivityManager = connectivityManagerWrapper.getConnectivityManager();
213         mConnectivityManagerWrapper = connectivityManagerWrapper;
214         mFragment = fragment;
215         mHandler = handler;
216         mSignalStr = context.getResources().getStringArray(R.array.wifi_signal);
217         mWifiConfig = accessPoint.getConfig();
218         mWifiManager = wifiManager;
219         mMetricsFeatureProvider = metricsFeatureProvider;
220
221         mFilter = new IntentFilter();
222         mFilter.addAction(WifiManager.NETWORK_STATE_CHANGED_ACTION);
223         mFilter.addAction(WifiManager.RSSI_CHANGED_ACTION);
224
225         lifecycle.addObserver(this);
226     }
227
228     @Override
229     public boolean isAvailable() {
230         return true;
231     }
232
233     @Override
234     public String getPreferenceKey() {
235         // Returns null since this controller contains more than one Preference
236         return null;
237     }
238
239     @Override
240     public void displayPreference(PreferenceScreen screen) {
241         super.displayPreference(screen);
242
243         setupEntityHeader(screen);
244
245         mButtonsPref = (LayoutPreference) screen.findPreference(KEY_BUTTONS_PREF);
246         mSignInButton = mButtonsPref.findViewById(R.id.signin_button);
247         mSignInButton.setText(R.string.support_sign_in_button_text);
248         mSignInButton.setOnClickListener(view -> signIntoNetwork());
249
250         mSignalStrengthPref =
251                 (WifiDetailPreference) screen.findPreference(KEY_SIGNAL_STRENGTH_PREF);
252         mLinkSpeedPref = (WifiDetailPreference) screen.findPreference(KEY_LINK_SPEED);
253         mFrequencyPref = (WifiDetailPreference) screen.findPreference(KEY_FREQUENCY_PREF);
254         mSecurityPref = (WifiDetailPreference) screen.findPreference(KEY_SECURITY_PREF);
255
256         mMacAddressPref = (WifiDetailPreference) screen.findPreference(KEY_MAC_ADDRESS_PREF);
257         mIpAddressPref = (WifiDetailPreference) screen.findPreference(KEY_IP_ADDRESS_PREF);
258         mGatewayPref = (WifiDetailPreference) screen.findPreference(KEY_GATEWAY_PREF);
259         mSubnetPref = (WifiDetailPreference) screen.findPreference(KEY_SUBNET_MASK_PREF);
260         mDnsPref = (WifiDetailPreference) screen.findPreference(KEY_DNS_PREF);
261
262         mIpv6Category = (PreferenceCategory) screen.findPreference(KEY_IPV6_CATEGORY);
263         mIpv6AddressPref = screen.findPreference(KEY_IPV6_ADDRESSES_PREF);
264
265         mSecurityPref.setDetailText(mAccessPoint.getSecurityString(false /* concise */));
266         mForgetButton = mButtonsPref.findViewById(R.id.forget_button);
267         mForgetButton.setText(R.string.forget);
268         mForgetButton.setOnClickListener(view -> forgetNetwork());
269     }
270
271     private void setupEntityHeader(PreferenceScreen screen) {
272         LayoutPreference headerPref = (LayoutPreference) screen.findPreference(KEY_HEADER);
273         mEntityHeaderController =
274                 EntityHeaderController.newInstance(
275                         mFragment.getActivity(), mFragment,
276                         headerPref.findViewById(R.id.entity_header));
277
278         ImageView iconView = headerPref.findViewById(R.id.entity_header_icon);
279         iconView.setBackground(
280                 mContext.getDrawable(R.drawable.ic_settings_widget_background));
281         iconView.setScaleType(ImageView.ScaleType.CENTER_INSIDE);
282
283         mEntityHeaderController.setLabel(mAccessPoint.getSsidStr());
284     }
285
286     @Override
287     public void onResume() {
288         // Ensure mNetwork is set before any callbacks above are delivered, since our
289         // NetworkCallback only looks at changes to mNetwork.
290         mNetwork = mWifiManager.getCurrentNetwork();
291         mLinkProperties = mConnectivityManager.getLinkProperties(mNetwork);
292         mNetworkCapabilities = mConnectivityManager.getNetworkCapabilities(mNetwork);
293         updateInfo();
294         mContext.registerReceiver(mReceiver, mFilter);
295         mConnectivityManagerWrapper.registerNetworkCallback(mNetworkRequest, mNetworkCallback,
296                 mHandler);
297     }
298
299     @Override
300     public void onPause() {
301         mNetwork = null;
302         mLinkProperties = null;
303         mNetworkCapabilities = null;
304         mNetworkInfo = null;
305         mWifiInfo = null;
306         mContext.unregisterReceiver(mReceiver);
307         mConnectivityManager.unregisterNetworkCallback(mNetworkCallback);
308     }
309
310     private void updateInfo() {
311         // No need to fetch LinkProperties and NetworkCapabilities, they are updated by the
312         // callbacks. mNetwork doesn't change except in onResume.
313         mNetworkInfo = mConnectivityManager.getNetworkInfo(mNetwork);
314         mWifiInfo = mWifiManager.getConnectionInfo();
315         if (mNetwork == null || mNetworkInfo == null || mWifiInfo == null) {
316             exitActivity();
317             return;
318         }
319
320         // Update whether the forgot button should be displayed.
321         mForgetButton.setVisibility(canForgetNetwork() ? View.VISIBLE : View.INVISIBLE);
322
323         refreshNetworkState();
324
325         // Update Connection Header icon and Signal Strength Preference
326         mRssi = mWifiInfo.getRssi();
327         refreshRssiViews();
328
329         // MAC Address Pref
330         mMacAddressPref.setDetailText(mWifiInfo.getMacAddress());
331
332         // Link Speed Pref
333         int linkSpeedMbps = mWifiInfo.getLinkSpeed();
334         mLinkSpeedPref.setVisible(linkSpeedMbps >= 0);
335         mLinkSpeedPref.setDetailText(mContext.getString(
336                 R.string.link_speed, mWifiInfo.getLinkSpeed()));
337
338         // Frequency Pref
339         final int frequency = mWifiInfo.getFrequency();
340         String band = null;
341         if (frequency >= AccessPoint.LOWER_FREQ_24GHZ
342                 && frequency < AccessPoint.HIGHER_FREQ_24GHZ) {
343             band = mContext.getResources().getString(R.string.wifi_band_24ghz);
344         } else if (frequency >= AccessPoint.LOWER_FREQ_5GHZ
345                 && frequency < AccessPoint.HIGHER_FREQ_5GHZ) {
346             band = mContext.getResources().getString(R.string.wifi_band_5ghz);
347         } else {
348             Log.e(TAG, "Unexpected frequency " + frequency);
349         }
350         mFrequencyPref.setDetailText(band);
351
352         updateIpLayerInfo();
353     }
354
355     private void exitActivity() {
356         if (DEBUG) {
357             Log.d(TAG, "Exiting the WifiNetworkDetailsPage");
358         }
359         mFragment.getActivity().finish();
360     }
361
362     private void refreshNetworkState() {
363         mAccessPoint.update(mWifiConfig, mWifiInfo, mNetworkInfo);
364         mEntityHeaderController.setSummary(mAccessPoint.getSettingsSummary())
365                 .done(mFragment.getActivity(), true /* rebind */);
366     }
367
368     private void refreshRssiViews() {
369         int iconSignalLevel = WifiManager.calculateSignalLevel(
370                 mRssi, WifiManager.RSSI_LEVELS);
371         Drawable wifiIcon = NetworkBadging.getWifiIcon(
372                 iconSignalLevel, NetworkBadging.BADGING_NONE, mContext.getTheme()).mutate();
373
374         wifiIcon.setTint(Utils.getColorAccent(mContext));
375         mEntityHeaderController.setIcon(wifiIcon).done(mFragment.getActivity(), true /* rebind */);
376
377         Drawable wifiIconDark = wifiIcon.getConstantState().newDrawable().mutate();
378         wifiIconDark.setTint(mContext.getResources().getColor(
379                 R.color.wifi_details_icon_color, mContext.getTheme()));
380         mSignalStrengthPref.setIcon(wifiIconDark);
381
382         int summarySignalLevel = mAccessPoint.getLevel();
383         mSignalStrengthPref.setDetailText(mSignalStr[summarySignalLevel]);
384     }
385
386     private void updatePreference(WifiDetailPreference pref, String detailText) {
387         if (!TextUtils.isEmpty(detailText)) {
388             pref.setDetailText(detailText);
389             pref.setVisible(true);
390         } else {
391             pref.setVisible(false);
392         }
393     }
394
395     private void updateIpLayerInfo() {
396         mSignInButton.setVisibility(canSignIntoNetwork() ? View.VISIBLE : View.INVISIBLE);
397         mButtonsPref.setVisible(mForgetButton.getVisibility() == View.VISIBLE
398                 || mSignInButton.getVisibility() == View.VISIBLE);
399
400         if (mNetwork == null || mLinkProperties == null) {
401             mIpAddressPref.setVisible(false);
402             mSubnetPref.setVisible(false);
403             mGatewayPref.setVisible(false);
404             mDnsPref.setVisible(false);
405             mIpv6Category.setVisible(false);
406             return;
407         }
408
409         // Find IPv4 and IPv6 addresses.
410         String ipv4Address = null;
411         String subnet = null;
412         StringJoiner ipv6Addresses = new StringJoiner("\n");
413
414         for (LinkAddress addr : mLinkProperties.getLinkAddresses()) {
415             if (addr.getAddress() instanceof Inet4Address) {
416                 ipv4Address = addr.getAddress().getHostAddress();
417                 subnet = ipv4PrefixLengthToSubnetMask(addr.getPrefixLength());
418             } else if (addr.getAddress() instanceof Inet6Address) {
419                 ipv6Addresses.add(addr.getAddress().getHostAddress());
420             }
421         }
422
423         // Find IPv4 default gateway.
424         String gateway = null;
425         for (RouteInfo routeInfo : mLinkProperties.getRoutes()) {
426             if (routeInfo.isIPv4Default() && routeInfo.hasGateway()) {
427                 gateway = routeInfo.getGateway().getHostAddress();
428                 break;
429             }
430         }
431
432         // Find IPv4 DNS addresses.
433         String dnsServers = mLinkProperties.getDnsServers().stream()
434                 .filter(Inet4Address.class::isInstance)
435                 .map(InetAddress::getHostAddress)
436                 .collect(Collectors.joining(","));
437
438         // Update UI.
439         updatePreference(mIpAddressPref, ipv4Address);
440         updatePreference(mSubnetPref, subnet);
441         updatePreference(mGatewayPref, gateway);
442         updatePreference(mDnsPref, dnsServers);
443
444         if (ipv6Addresses.length() > 0) {
445             mIpv6AddressPref.setSummary(ipv6Addresses.toString());
446             mIpv6Category.setVisible(true);
447         } else {
448             mIpv6Category.setVisible(false);
449         }
450     }
451
452     private static String ipv4PrefixLengthToSubnetMask(int prefixLength) {
453         try {
454             InetAddress all = InetAddress.getByAddress(
455                     new byte[]{(byte) 255, (byte) 255, (byte) 255, (byte) 255});
456             return NetworkUtils.getNetworkPart(all, prefixLength).getHostAddress();
457         } catch (UnknownHostException e) {
458             return null;
459         }
460     }
461
462     /**
463      * Returns whether the network represented by this preference can be forgotten.
464      */
465     private boolean canForgetNetwork() {
466         return mWifiInfo != null && mWifiInfo.isEphemeral() || mWifiConfig != null;
467     }
468
469     /**
470      * Returns whether the user can sign into the network represented by this preference.
471      */
472     private boolean canSignIntoNetwork() {
473         return mNetworkCapabilities != null && mNetworkCapabilities.hasCapability(
474                 NET_CAPABILITY_CAPTIVE_PORTAL);
475     }
476
477     /**
478      * Forgets the wifi network associated with this preference.
479      */
480     private void forgetNetwork() {
481         if (mWifiInfo != null && mWifiInfo.isEphemeral()) {
482             mWifiManager.disableEphemeralNetwork(mWifiInfo.getSSID());
483         } else if (mWifiConfig != null) {
484             if (mWifiConfig.isPasspoint()) {
485                 mWifiManager.removePasspointConfiguration(mWifiConfig.FQDN);
486             } else {
487                 mWifiManager.forget(mWifiConfig.networkId, null /* action listener */);
488             }
489         }
490         mMetricsFeatureProvider.action(
491                 mFragment.getActivity(), MetricsProto.MetricsEvent.ACTION_WIFI_FORGET);
492         mFragment.getActivity().finish();
493     }
494
495     /**
496      * Sign in to the captive portal found on this wifi network associated with this preference.
497      */
498     private void signIntoNetwork() {
499         mMetricsFeatureProvider.action(
500                 mFragment.getActivity(), MetricsProto.MetricsEvent.ACTION_WIFI_SIGNIN);
501         mConnectivityManagerWrapper.startCaptivePortalApp(mNetwork);
502     }
503 }