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