• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2010 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.settings.wifi;
18 
19 import android.content.Context;
20 import android.content.res.Resources;
21 import android.net.InetAddresses;
22 import android.net.IpConfiguration;
23 import android.net.IpConfiguration.IpAssignment;
24 import android.net.IpConfiguration.ProxySettings;
25 import android.net.LinkAddress;
26 import android.net.NetworkInfo.DetailedState;
27 import android.net.ProxyInfo;
28 import android.net.StaticIpConfiguration;
29 import android.net.Uri;
30 import android.net.wifi.WifiConfiguration;
31 import android.net.wifi.WifiEnterpriseConfig;
32 import android.net.wifi.WifiEnterpriseConfig.Eap;
33 import android.net.wifi.WifiEnterpriseConfig.Phase2;
34 import android.net.wifi.WifiInfo;
35 import android.net.wifi.WifiManager;
36 import android.os.IBinder;
37 import android.security.keystore.KeyProperties;
38 import android.telephony.SubscriptionInfo;
39 import android.telephony.SubscriptionManager;
40 import android.text.Editable;
41 import android.text.InputType;
42 import android.text.SpannableString;
43 import android.text.TextUtils;
44 import android.text.TextWatcher;
45 import android.util.Log;
46 import android.view.KeyEvent;
47 import android.view.View;
48 import android.view.View.AccessibilityDelegate;
49 import android.view.ViewGroup;
50 import android.view.accessibility.AccessibilityEvent;
51 import android.view.accessibility.AccessibilityNodeInfo;
52 import android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction;
53 import android.view.inputmethod.EditorInfo;
54 import android.view.inputmethod.InputMethodManager;
55 import android.widget.AdapterView;
56 import android.widget.ArrayAdapter;
57 import android.widget.Button;
58 import android.widget.CheckBox;
59 import android.widget.CompoundButton;
60 import android.widget.CompoundButton.OnCheckedChangeListener;
61 import android.widget.EditText;
62 import android.widget.ImageButton;
63 import android.widget.LinearLayout;
64 import android.widget.Spinner;
65 import android.widget.TextView;
66 
67 import androidx.annotation.VisibleForTesting;
68 
69 import com.android.net.module.util.NetUtils;
70 import com.android.net.module.util.ProxyUtils;
71 import com.android.settings.ProxySelector;
72 import com.android.settings.R;
73 import com.android.settings.network.SubscriptionUtil;
74 import com.android.settings.utils.AndroidKeystoreAliasLoader;
75 import com.android.settings.wifi.details2.WifiPrivacyPreferenceController;
76 import com.android.settings.wifi.dpp.WifiDppUtils;
77 import com.android.settingslib.Utils;
78 import com.android.settingslib.utils.ThreadUtils;
79 import com.android.settingslib.wifi.AccessPoint;
80 import com.android.wifi.flags.Flags;
81 import com.android.wifitrackerlib.WifiEntry;
82 
83 import java.net.Inet4Address;
84 import java.net.InetAddress;
85 import java.util.ArrayList;
86 import java.util.Arrays;
87 import java.util.Collection;
88 import java.util.Collections;
89 import java.util.Iterator;
90 import java.util.List;
91 import java.util.stream.Collectors;
92 
93 /**
94  * The class for allowing UIs like {@link WifiDialog} and {@link WifiConfigUiBase} to
95  * share the logic for controlling buttons, text fields, etc.
96  *
97  * Migrating from Wi-Fi SettingsLib to to WifiTrackerLib, this object will be removed in the near
98  * future, please develop in {@link WifiConfigController2}.
99  */
100 public class WifiConfigController implements TextWatcher,
101         AdapterView.OnItemSelectedListener, OnCheckedChangeListener,
102         TextView.OnEditorActionListener, View.OnKeyListener {
103     private static final String TAG = "WifiConfigController";
104 
105     private static final String SYSTEM_CA_STORE_PATH = "/system/etc/security/cacerts";
106 
107     private final WifiConfigUiBase mConfigUi;
108     private final View mView;
109     private final AccessPoint mAccessPoint;
110 
111     /* This value comes from "wifi_ip_settings" resource array */
112     private static final int DHCP = 0;
113     private static final int STATIC_IP = 1;
114 
115     /* Constants used for referring to the hidden state of a network. */
116     public static final int HIDDEN_NETWORK = 1;
117     public static final int NOT_HIDDEN_NETWORK = 0;
118 
119     /* These values come from "wifi_proxy_settings" resource array */
120     public static final int PROXY_NONE = 0;
121     public static final int PROXY_STATIC = 1;
122     public static final int PROXY_PAC = 2;
123 
124     /* These values come from "wifi_eap_method" resource array */
125     public static final int WIFI_EAP_METHOD_PEAP = 0;
126     public static final int WIFI_EAP_METHOD_TLS  = 1;
127     public static final int WIFI_EAP_METHOD_TTLS = 2;
128     public static final int WIFI_EAP_METHOD_PWD  = 3;
129     public static final int WIFI_EAP_METHOD_SIM  = 4;
130     public static final int WIFI_EAP_METHOD_AKA  = 5;
131     public static final int WIFI_EAP_METHOD_AKA_PRIME  = 6;
132 
133     /* These values come from "wifi_peap_phase2_entries" resource array */
134     public static final int WIFI_PEAP_PHASE2_MSCHAPV2   = 0;
135     public static final int WIFI_PEAP_PHASE2_GTC        = 1;
136     public static final int WIFI_PEAP_PHASE2_SIM        = 2;
137     public static final int WIFI_PEAP_PHASE2_AKA        = 3;
138     public static final int WIFI_PEAP_PHASE2_AKA_PRIME  = 4;
139 
140     /* These values come from "wifi_ttls_phase2_entries" resource array */
141     public static final int WIFI_TTLS_PHASE2_PAP       = 0;
142     public static final int WIFI_TTLS_PHASE2_MSCHAP    = 1;
143     public static final int WIFI_TTLS_PHASE2_MSCHAPV2  = 2;
144     public static final int WIFI_TTLS_PHASE2_GTC       = 3;
145 
146     private static final String UNDESIRED_CERTIFICATE_MACRANDSECRET = "MacRandSecret";
147     private static final String UNDESIRED_CERTIFICATE_MACRANDSAPSECRET = "MacRandSapSecret";
148     @VisibleForTesting
149     static final String[] UNDESIRED_CERTIFICATES = {
150         UNDESIRED_CERTIFICATE_MACRANDSECRET,
151         UNDESIRED_CERTIFICATE_MACRANDSAPSECRET
152     };
153 
154     // Should be the same index value as wifi_privacy_entries in arrays.xml
155     @VisibleForTesting static final int PRIVACY_SPINNER_INDEX_RANDOMIZED_MAC = 0;
156     @VisibleForTesting static final int PRIVACY_SPINNER_INDEX_DEVICE_MAC = 1;
157 
158     // Should be the same index value as wifi_dhcp_entries in arrays.xml
159     @VisibleForTesting static final int DHCP_SPINNER_INDEX_SEND_DHCP_HOST_NAME_ENABLE = 0;
160     @VisibleForTesting static final int DHCP_SPINNER_INDEX_SEND_DHCP_HOST_NAME_DISABLE = 1;
161 
162     /* Phase2 methods supported by PEAP are limited */
163     private ArrayAdapter<CharSequence> mPhase2PeapAdapter;
164     /* Phase2 methods supported by TTLS are limited */
165     private ArrayAdapter<CharSequence> mPhase2TtlsAdapter;
166 
167     // e.g. AccessPoint.SECURITY_NONE
168     @VisibleForTesting
169     int mAccessPointSecurity;
170     private TextView mPasswordView;
171     private ImageButton mSsidScanButton;
172 
173     private String mUnspecifiedCertString;
174     private String mMultipleCertSetString;
175     private String mUseSystemCertsString;
176     private String mDoNotProvideEapUserCertString;
177 
178     private Spinner mSecuritySpinner;
179     @VisibleForTesting Spinner mEapMethodSpinner;
180     @VisibleForTesting Spinner mEapSimSpinner;    // For EAP-SIM, EAP-AKA and EAP-AKA-PRIME.
181     private Spinner mEapCaCertSpinner;
182     private Spinner mEapOcspSpinner;
183     private TextView mEapDomainView;
184     private Spinner mPhase2Spinner;
185     // Associated with mPhase2Spinner, one of mPhase2TtlsAdapter or mPhase2PeapAdapter
186     private ArrayAdapter<CharSequence> mPhase2Adapter;
187     private Spinner mEapUserCertSpinner;
188     private TextView mEapIdentityView;
189     private TextView mEapAnonymousView;
190 
191     private Spinner mIpSettingsSpinner;
192     private TextView mIpAddressView;
193     private TextView mGatewayView;
194     private TextView mNetworkPrefixLengthView;
195     private TextView mDns1View;
196     private TextView mDns2View;
197 
198     private Spinner mProxySettingsSpinner;
199     private Spinner mMeteredSettingsSpinner;
200     private Spinner mHiddenSettingsSpinner;
201     private Spinner mPrivacySettingsSpinner;
202     private Spinner mDhcpSettingsSpinner;
203     private TextView mHiddenWarningView;
204     private TextView mProxyHostView;
205     private TextView mProxyPortView;
206     private TextView mProxyExclusionListView;
207     private TextView mProxyPacView;
208     private CheckBox mSharedCheckBox;
209 
210     private IpAssignment mIpAssignment = IpAssignment.UNASSIGNED;
211     private ProxySettings mProxySettings = ProxySettings.UNASSIGNED;
212     private ProxyInfo mHttpProxy = null;
213     private StaticIpConfiguration mStaticIpConfiguration = null;
214     private boolean mRequestFocus = true;
215 
216     private String[] mLevels;
217     private int mMode;
218     private TextView mSsidView;
219 
220     private Context mContext;
221 
222     @VisibleForTesting
223     Integer mSecurityInPosition[];
224 
225     private final WifiManager mWifiManager;
226 
227     private final List<SubscriptionInfo> mActiveSubscriptionInfos = new ArrayList<>();
228 
WifiConfigController(WifiConfigUiBase parent, View view, AccessPoint accessPoint, int mode)229     public WifiConfigController(WifiConfigUiBase parent, View view, AccessPoint accessPoint,
230             int mode) {
231         this (parent, view, accessPoint, mode, true /* requestFocus */);
232     }
233 
WifiConfigController(WifiConfigUiBase parent, View view, AccessPoint accessPoint, int mode, boolean requestFocus)234     public WifiConfigController(WifiConfigUiBase parent, View view, AccessPoint accessPoint,
235             int mode, boolean requestFocus) {
236         mConfigUi = parent;
237 
238         mView = view;
239         mAccessPoint = accessPoint;
240         mContext = mConfigUi.getContext();
241         mRequestFocus = requestFocus;
242 
243         // Init Wi-Fi manager
244         mWifiManager = (WifiManager) mContext.getSystemService(Context.WIFI_SERVICE);
245         initWifiConfigController(accessPoint, mode);
246     }
247 
248     @VisibleForTesting
WifiConfigController(WifiConfigUiBase parent, View view, AccessPoint accessPoint, int mode, WifiManager wifiManager)249     public WifiConfigController(WifiConfigUiBase parent, View view, AccessPoint accessPoint,
250             int mode, WifiManager wifiManager) {
251         mConfigUi = parent;
252 
253         mView = view;
254         mAccessPoint = accessPoint;
255         mContext = mConfigUi.getContext();
256         mWifiManager = wifiManager;
257         initWifiConfigController(accessPoint, mode);
258     }
259 
initWifiConfigController(AccessPoint accessPoint, int mode)260     private void initWifiConfigController(AccessPoint accessPoint, int mode) {
261 
262         mAccessPointSecurity = (accessPoint == null) ? AccessPoint.SECURITY_NONE :
263                 accessPoint.getSecurity();
264         mMode = mode;
265 
266         final Resources res = mContext.getResources();
267 
268         mLevels = res.getStringArray(R.array.wifi_signal);
269         if (Utils.isWifiOnly(mContext) || !mContext.getResources().getBoolean(
270                 com.android.internal.R.bool.config_eap_sim_based_auth_supported)) {
271             mPhase2PeapAdapter = getSpinnerAdapter(R.array.wifi_peap_phase2_entries);
272         } else {
273             mPhase2PeapAdapter = getSpinnerAdapterWithEapMethodsTts(
274                 R.array.wifi_peap_phase2_entries_with_sim_auth);
275         }
276 
277         mPhase2TtlsAdapter = getSpinnerAdapter(R.array.wifi_ttls_phase2_entries);
278 
279         mUnspecifiedCertString = mContext.getString(R.string.wifi_unspecified);
280         mMultipleCertSetString = mContext.getString(R.string.wifi_multiple_cert_added);
281         mUseSystemCertsString = mContext.getString(R.string.wifi_use_system_certs);
282         mDoNotProvideEapUserCertString =
283             mContext.getString(R.string.wifi_do_not_provide_eap_user_cert);
284 
285         if (Flags.androidVWifiApi() && mAccessPointSecurity == WifiEntry.SECURITY_WEP) {
286             LinearLayout wepWarningLayout =
287                     (LinearLayout) mView.findViewById(R.id.wep_warning_layout);
288             wepWarningLayout.setVisibility(View.VISIBLE);
289         }
290 
291         mSsidScanButton = (ImageButton) mView.findViewById(R.id.ssid_scanner_button);
292         mIpSettingsSpinner = (Spinner) mView.findViewById(R.id.ip_settings);
293         mIpSettingsSpinner.setOnItemSelectedListener(this);
294         mProxySettingsSpinner = (Spinner) mView.findViewById(R.id.proxy_settings);
295         mProxySettingsSpinner.setOnItemSelectedListener(this);
296         mSharedCheckBox = (CheckBox) mView.findViewById(R.id.shared);
297         mMeteredSettingsSpinner = mView.findViewById(R.id.metered_settings);
298         mHiddenSettingsSpinner = mView.findViewById(R.id.hidden_settings);
299         mPrivacySettingsSpinner = mView.findViewById(R.id.privacy_settings);
300         mDhcpSettingsSpinner = mView.findViewById(R.id.dhcp_settings);
301         if (mWifiManager.isConnectedMacRandomizationSupported()) {
302             View privacySettingsLayout = mView.findViewById(R.id.privacy_settings_fields);
303             privacySettingsLayout.setVisibility(View.VISIBLE);
304         }
305         mHiddenSettingsSpinner.setOnItemSelectedListener(this);
306         mHiddenWarningView = mView.findViewById(R.id.hidden_settings_warning);
307         mHiddenWarningView.setVisibility(
308                 mHiddenSettingsSpinner.getSelectedItemPosition() == NOT_HIDDEN_NETWORK
309                         ? View.GONE
310                         : View.VISIBLE);
311         mSecurityInPosition = new Integer[AccessPoint.SECURITY_MAX_VAL];
312 
313         if (mAccessPoint == null) { // new network
314             configureSecuritySpinner();
315             mConfigUi.setSubmitButton(res.getString(R.string.wifi_save));
316         } else {
317             mConfigUi.setTitle(mAccessPoint.getTitle());
318 
319             ViewGroup group = (ViewGroup) mView.findViewById(R.id.info);
320 
321             boolean showAdvancedFields = false;
322             if (mAccessPoint.isSaved()) {
323                 WifiConfiguration config = mAccessPoint.getConfig();
324                 mMeteredSettingsSpinner.setSelection(config.meteredOverride);
325                 mHiddenSettingsSpinner.setSelection(config.hiddenSSID
326                         ? HIDDEN_NETWORK
327                         : NOT_HIDDEN_NETWORK);
328 
329                 mPrivacySettingsSpinner.setSelection(
330                         config.macRandomizationSetting == WifiConfiguration.RANDOMIZATION_PERSISTENT
331                         ? PRIVACY_SPINNER_INDEX_RANDOMIZED_MAC : PRIVACY_SPINNER_INDEX_DEVICE_MAC);
332 
333                 mDhcpSettingsSpinner.setSelection(
334                         config.isSendDhcpHostnameEnabled()
335                                 ? DHCP_SPINNER_INDEX_SEND_DHCP_HOST_NAME_ENABLE :
336                                 DHCP_SPINNER_INDEX_SEND_DHCP_HOST_NAME_DISABLE
337                 );
338 
339                 if (config.getIpConfiguration().getIpAssignment() == IpAssignment.STATIC) {
340                     mIpSettingsSpinner.setSelection(STATIC_IP);
341                     showAdvancedFields = true;
342                     // Display IP address.
343                     StaticIpConfiguration staticConfig = config.getIpConfiguration()
344                             .getStaticIpConfiguration();
345                     if (staticConfig != null && staticConfig.getIpAddress() != null) {
346                         addRow(group, R.string.wifi_ip_address,
347                                 staticConfig.getIpAddress().getAddress().getHostAddress());
348                     }
349                 } else {
350                     mIpSettingsSpinner.setSelection(DHCP);
351                 }
352 
353                 mSharedCheckBox.setEnabled(config.shared);
354                 if (!config.shared) {
355                     showAdvancedFields = true;
356                 }
357 
358                 ProxySettings proxySettings = config.getIpConfiguration().getProxySettings();
359                 if (proxySettings == ProxySettings.STATIC) {
360                     mProxySettingsSpinner.setSelection(PROXY_STATIC);
361                     showAdvancedFields = true;
362                 } else if (proxySettings == ProxySettings.PAC) {
363                     mProxySettingsSpinner.setSelection(PROXY_PAC);
364                     showAdvancedFields = true;
365                 } else {
366                     mProxySettingsSpinner.setSelection(PROXY_NONE);
367                 }
368                 if (config != null && config.isPasspoint()) {
369                     addRow(group, R.string.passpoint_label,
370                             String.format(mContext.getString(R.string.passpoint_content),
371                             config.providerFriendlyName));
372                 }
373             }
374 
375             if ((!mAccessPoint.isSaved() && !mAccessPoint.isActive()
376                     && !mAccessPoint.isPasspointConfig())
377                     || mMode != WifiConfigUiBase.MODE_VIEW) {
378                 showSecurityFields(/* refreshEapMethods */ true, /* refreshCertificates */ true);
379                 showIpConfigFields();
380                 showProxyFields();
381                 final CheckBox advancedTogglebox =
382                         (CheckBox) mView.findViewById(R.id.wifi_advanced_togglebox);
383                 if (!showAdvancedFields) {
384                     // Need to show Advanced Option button.
385                     mView.findViewById(R.id.wifi_advanced_toggle).setVisibility(View.VISIBLE);
386                     advancedTogglebox.setOnCheckedChangeListener(this);
387                     advancedTogglebox.setChecked(showAdvancedFields);
388                     setAdvancedOptionAccessibilityString();
389                 }
390                 mView.findViewById(R.id.wifi_advanced_fields)
391                         .setVisibility(showAdvancedFields ? View.VISIBLE : View.GONE);
392             }
393 
394             if (mMode == WifiConfigUiBase.MODE_MODIFY) {
395                 mConfigUi.setSubmitButton(res.getString(R.string.wifi_save));
396             } else if (mMode == WifiConfigUiBase.MODE_CONNECT) {
397                 mConfigUi.setSubmitButton(res.getString(R.string.wifi_connect));
398             } else {
399                 final DetailedState state = mAccessPoint.getDetailedState();
400                 final String signalLevel = getSignalString();
401 
402                 if ((state == null || state == DetailedState.DISCONNECTED) && signalLevel != null) {
403                     mConfigUi.setSubmitButton(res.getString(R.string.wifi_connect));
404                 } else {
405                     if (state != null) {
406                         boolean isEphemeral = mAccessPoint.isEphemeral();
407                         WifiConfiguration config = mAccessPoint.getConfig();
408                         String providerFriendlyName = null;
409                         if (config != null && config.isPasspoint()) {
410                             providerFriendlyName = config.providerFriendlyName;
411                         }
412                         String suggestionOrSpecifierPackageName = null;
413                         if (config != null
414                                 && (config.fromWifiNetworkSpecifier
415                                 || config.fromWifiNetworkSuggestion)) {
416                             suggestionOrSpecifierPackageName = config.creatorName;
417                         }
418                         String summary = AccessPoint.getSummary(
419                                 mConfigUi.getContext(), /* ssid */ null, state, isEphemeral,
420                                 suggestionOrSpecifierPackageName);
421                         addRow(group, R.string.wifi_status, summary);
422                     }
423 
424                     if (signalLevel != null) {
425                         addRow(group, R.string.wifi_signal, signalLevel);
426                     }
427 
428                     WifiInfo info = mAccessPoint.getInfo();
429                     if (info != null && info.getTxLinkSpeedMbps() != WifiInfo.LINK_SPEED_UNKNOWN) {
430                         addRow(group, R.string.tx_wifi_speed, String.format(
431                                 res.getString(R.string.tx_link_speed), info.getTxLinkSpeedMbps()));
432                     }
433 
434                     if (info != null && info.getRxLinkSpeedMbps() != WifiInfo.LINK_SPEED_UNKNOWN) {
435                         addRow(group, R.string.rx_wifi_speed, String.format(
436                                 res.getString(R.string.rx_link_speed), info.getRxLinkSpeedMbps()));
437                     }
438 
439                     if (info != null && info.getFrequency() != -1) {
440                         final int frequency = info.getFrequency();
441                         String band = null;
442 
443                         if (frequency >= AccessPoint.LOWER_FREQ_24GHZ
444                                 && frequency < AccessPoint.HIGHER_FREQ_24GHZ) {
445                             band = res.getString(R.string.wifi_band_24ghz);
446                         } else if (frequency >= AccessPoint.LOWER_FREQ_5GHZ
447                                 && frequency < AccessPoint.HIGHER_FREQ_5GHZ) {
448                             band = res.getString(R.string.wifi_band_5ghz);
449                         } else {
450                             Log.e(TAG, "Unexpected frequency " + frequency);
451                         }
452                         if (band != null) {
453                             addRow(group, R.string.wifi_frequency, band);
454                         }
455                     }
456 
457                     addRow(group, R.string.wifi_security, mAccessPoint.getSecurityString(false));
458                     mView.findViewById(R.id.ip_fields).setVisibility(View.GONE);
459                 }
460                 if (mAccessPoint.isSaved() || mAccessPoint.isActive()
461                         || mAccessPoint.isPasspointConfig()) {
462                     mConfigUi.setForgetButton(res.getString(R.string.wifi_forget));
463                 }
464             }
465 
466             mSsidScanButton.setVisibility(View.GONE);
467         }
468         mSharedCheckBox.setVisibility(View.GONE);
469 
470         mConfigUi.setCancelButton(res.getString(R.string.wifi_cancel));
471         if (mConfigUi.getSubmitButton() != null) {
472             enableSubmitIfAppropriate();
473         }
474 
475         // After done view show and hide, request focus from parameter.
476         if (mRequestFocus) {
477             mView.findViewById(R.id.l_wifidialog).requestFocus();
478         }
479     }
480 
addRow(ViewGroup group, int name, String value)481     private void addRow(ViewGroup group, int name, String value) {
482         View row = mConfigUi.getLayoutInflater().inflate(R.layout.wifi_dialog_row, group, false);
483         ((TextView) row.findViewById(R.id.name)).setText(name);
484         ((TextView) row.findViewById(R.id.value)).setText(value);
485         group.addView(row);
486     }
487 
488     @VisibleForTesting
getSignalString()489     String getSignalString() {
490         if (!mAccessPoint.isReachable()) {
491             return null;
492         }
493         final int level = mAccessPoint.getLevel();
494 
495         return (level > -1 && level < mLevels.length) ? mLevels[level] : null;
496     }
497 
hideForgetButton()498     void hideForgetButton() {
499         Button forget = mConfigUi.getForgetButton();
500         if (forget == null) return;
501 
502         forget.setVisibility(View.GONE);
503     }
504 
hideSubmitButton()505     void hideSubmitButton() {
506         Button submit = mConfigUi.getSubmitButton();
507         if (submit == null) return;
508 
509         submit.setVisibility(View.GONE);
510     }
511 
512     /* show submit button if password, ip and proxy settings are valid */
enableSubmitIfAppropriate()513     void enableSubmitIfAppropriate() {
514         Button submit = mConfigUi.getSubmitButton();
515         if (submit == null) return;
516 
517         submit.setEnabled(isSubmittable());
518     }
519 
isValidPsk(String password)520     boolean isValidPsk(String password) {
521         if (password.length() == 64 && password.matches("[0-9A-Fa-f]{64}")) {
522             return true;
523         } else if (password.length() >= 8 && password.length() <= 63) {
524             return true;
525         }
526         return false;
527     }
528 
isValidSaePassword(String password)529     boolean isValidSaePassword(String password) {
530         if (password.length() >= 1 && password.length() <= 63) {
531             return true;
532         }
533         return false;
534     }
535 
isSubmittable()536     boolean isSubmittable() {
537         boolean enabled = false;
538         boolean passwordInvalid = false;
539         if (mPasswordView != null
540                 && ((mAccessPointSecurity == AccessPoint.SECURITY_WEP
541                         && mPasswordView.length() == 0)
542                     || (mAccessPointSecurity == AccessPoint.SECURITY_PSK
543                            && !isValidPsk(mPasswordView.getText().toString()))
544                     || (mAccessPointSecurity == AccessPoint.SECURITY_SAE
545                         && !isValidSaePassword(mPasswordView.getText().toString())))) {
546             passwordInvalid = true;
547         }
548         if ((mSsidView != null && mSsidView.length() == 0)
549                 // If Accesspoint is not saved, apply passwordInvalid check
550                 || ((mAccessPoint == null || !mAccessPoint.isSaved()) && passwordInvalid
551                 // If AccessPoint is saved (modifying network) and password is changed, apply
552                 // Invalid password check
553                 || mAccessPoint != null && mAccessPoint.isSaved() && passwordInvalid
554                     && mPasswordView.length() > 0)) {
555             enabled = false;
556         } else {
557             enabled = ipAndProxyFieldsAreValid();
558         }
559         if ((mAccessPointSecurity == AccessPoint.SECURITY_EAP
560                 || mAccessPointSecurity == AccessPoint.SECURITY_EAP_WPA3_ENTERPRISE
561                 || mAccessPointSecurity == AccessPoint.SECURITY_EAP_SUITE_B)
562                 && mEapCaCertSpinner != null
563                 && mView.findViewById(R.id.l_ca_cert).getVisibility() != View.GONE) {
564             String caCertSelection = (String) mEapCaCertSpinner.getSelectedItem();
565             if (caCertSelection.equals(mUnspecifiedCertString)) {
566                 // Disallow submit if the user has not selected a CA certificate for an EAP network
567                 // configuration.
568                 enabled = false;
569             } else if (mEapDomainView != null
570                     && mView.findViewById(R.id.l_domain).getVisibility() != View.GONE
571                     && TextUtils.isEmpty(mEapDomainView.getText().toString())) {
572                 // Disallow submit if the user chooses to use a certificate for EAP server
573                 // validation, but does not provide a domain.
574                 enabled = false;
575             }
576         }
577         if ((mAccessPointSecurity == AccessPoint.SECURITY_EAP
578                 || mAccessPointSecurity == AccessPoint.SECURITY_EAP_WPA3_ENTERPRISE
579                 || mAccessPointSecurity == AccessPoint.SECURITY_EAP_SUITE_B)
580                 && mEapUserCertSpinner != null
581                 && mView.findViewById(R.id.l_user_cert).getVisibility() != View.GONE
582                 && mEapUserCertSpinner.getSelectedItem().equals(mUnspecifiedCertString)) {
583             // Disallow submit if the user has not selected a user certificate for an EAP network
584             // configuration.
585             enabled = false;
586         }
587         return enabled;
588     }
589 
showWarningMessagesIfAppropriate()590     void showWarningMessagesIfAppropriate() {
591         mView.findViewById(R.id.no_user_cert_warning).setVisibility(View.GONE);
592         mView.findViewById(R.id.no_domain_warning).setVisibility(View.GONE);
593         mView.findViewById(R.id.ssid_too_long_warning).setVisibility(View.GONE);
594 
595         if (mSsidView != null) {
596             final String ssid = mSsidView.getText().toString();
597             if (WifiUtils.isSSIDTooLong(ssid)) {
598                 mView.findViewById(R.id.ssid_too_long_warning).setVisibility(View.VISIBLE);
599             }
600         }
601         if (mEapCaCertSpinner != null
602                 && mView.findViewById(R.id.l_ca_cert).getVisibility() != View.GONE) {
603             if (mEapDomainView != null
604                     && mView.findViewById(R.id.l_domain).getVisibility() != View.GONE
605                     && TextUtils.isEmpty(mEapDomainView.getText().toString())) {
606                 // Display warning if user chooses to use a certificate without restricting the
607                 // server domain that these certificates can be used to validate.
608                 mView.findViewById(R.id.no_domain_warning).setVisibility(View.VISIBLE);
609             }
610         }
611 
612         if (mAccessPointSecurity == AccessPoint.SECURITY_EAP_SUITE_B &&
613                 mEapMethodSpinner.getSelectedItemPosition() == WIFI_EAP_METHOD_TLS) {
614             String userCertSelection = (String) mEapUserCertSpinner.getSelectedItem();
615             if (userCertSelection.equals(mUnspecifiedCertString)) {
616                 mView.findViewById(R.id.no_user_cert_warning).setVisibility(View.VISIBLE);
617             }
618         }
619     }
620 
getConfig()621     public WifiConfiguration getConfig() {
622         if (mMode == WifiConfigUiBase.MODE_VIEW) {
623             return null;
624         }
625 
626         WifiConfiguration config = new WifiConfiguration();
627 
628         if (mAccessPoint == null) {
629             config.SSID = AccessPoint.convertToQuotedString(
630                     mSsidView.getText().toString());
631             // If the user adds a network manually, assume that it is hidden.
632             config.hiddenSSID = mHiddenSettingsSpinner.getSelectedItemPosition() == HIDDEN_NETWORK;
633         } else if (!mAccessPoint.isSaved()) {
634             config.SSID = AccessPoint.convertToQuotedString(
635                     mAccessPoint.getSsidStr());
636         } else {
637             config.networkId = mAccessPoint.getConfig().networkId;
638             config.hiddenSSID = mAccessPoint.getConfig().hiddenSSID;
639         }
640 
641         config.shared = mSharedCheckBox.isChecked();
642 
643         switch (mAccessPointSecurity) {
644             case AccessPoint.SECURITY_NONE:
645                 config.setSecurityParams(WifiConfiguration.SECURITY_TYPE_OPEN);
646                 break;
647 
648             case AccessPoint.SECURITY_WEP:
649                 config.setSecurityParams(WifiConfiguration.SECURITY_TYPE_WEP);
650                 if (mPasswordView.length() != 0) {
651                     int length = mPasswordView.length();
652                     String password = mPasswordView.getText().toString();
653                     // WEP-40, WEP-104, and 256-bit WEP (WEP-232?)
654                     if ((length == 10 || length == 26 || length == 58)
655                             && password.matches("[0-9A-Fa-f]*")) {
656                         config.wepKeys[0] = password;
657                     } else {
658                         config.wepKeys[0] = '"' + password + '"';
659                     }
660                 }
661                 break;
662 
663             case AccessPoint.SECURITY_PSK:
664                 config.setSecurityParams(WifiConfiguration.SECURITY_TYPE_PSK);
665                 if (mPasswordView.length() != 0) {
666                     String password = mPasswordView.getText().toString();
667                     if (password.matches("[0-9A-Fa-f]{64}")) {
668                         config.preSharedKey = password;
669                     } else {
670                         config.preSharedKey = '"' + password + '"';
671                     }
672                 }
673                 break;
674 
675             case AccessPoint.SECURITY_EAP:
676             case AccessPoint.SECURITY_EAP_WPA3_ENTERPRISE:
677             case AccessPoint.SECURITY_EAP_SUITE_B:
678                 if (mEapMethodSpinner == null || mPhase2Spinner == null) {
679                     break;
680                 }
681                 if (mAccessPointSecurity == AccessPoint.SECURITY_EAP_SUITE_B) {
682                     // allowedSuiteBCiphers will be set according to certificate type
683                     config.setSecurityParams(WifiConfiguration.SECURITY_TYPE_EAP_SUITE_B);
684                 } else if (mAccessPointSecurity == AccessPoint.SECURITY_EAP_WPA3_ENTERPRISE) {
685                     config.setSecurityParams(WifiConfiguration.SECURITY_TYPE_EAP_WPA3_ENTERPRISE);
686                 } else {
687                     config.setSecurityParams(WifiConfiguration.SECURITY_TYPE_EAP);
688                 }
689                 config.enterpriseConfig = new WifiEnterpriseConfig();
690                 int eapMethod = mEapMethodSpinner.getSelectedItemPosition();
691                 int phase2Method = mPhase2Spinner.getSelectedItemPosition();
692                 config.enterpriseConfig.setEapMethod(eapMethod);
693                 switch (eapMethod) {
694                     case Eap.PEAP:
695                         // PEAP supports limited phase2 values
696                         // Map the index from the mPhase2PeapAdapter to the one used
697                         // by the API which has the full list of PEAP methods.
698                         switch(phase2Method) {
699                             case WIFI_PEAP_PHASE2_MSCHAPV2:
700                                 config.enterpriseConfig.setPhase2Method(Phase2.MSCHAPV2);
701                                 break;
702                             case WIFI_PEAP_PHASE2_GTC:
703                                 config.enterpriseConfig.setPhase2Method(Phase2.GTC);
704                                 break;
705                             case WIFI_PEAP_PHASE2_SIM:
706                                 config.enterpriseConfig.setPhase2Method(Phase2.SIM);
707                                 break;
708                             case WIFI_PEAP_PHASE2_AKA:
709                                 config.enterpriseConfig.setPhase2Method(Phase2.AKA);
710                                 break;
711                             case WIFI_PEAP_PHASE2_AKA_PRIME:
712                                 config.enterpriseConfig.setPhase2Method(Phase2.AKA_PRIME);
713                                 break;
714                             default:
715                                 Log.e(TAG, "Unknown phase2 method" + phase2Method);
716                                 break;
717                         }
718                         break;
719                     case Eap.TTLS:
720                         // The default index from mPhase2TtlsAdapter maps to the API
721                         switch(phase2Method) {
722                             case WIFI_TTLS_PHASE2_PAP:
723                                 config.enterpriseConfig.setPhase2Method(Phase2.PAP);
724                                 break;
725                             case WIFI_TTLS_PHASE2_MSCHAP:
726                                 config.enterpriseConfig.setPhase2Method(Phase2.MSCHAP);
727                                 break;
728                             case WIFI_TTLS_PHASE2_MSCHAPV2:
729                                 config.enterpriseConfig.setPhase2Method(Phase2.MSCHAPV2);
730                                 break;
731                             case WIFI_TTLS_PHASE2_GTC:
732                                 config.enterpriseConfig.setPhase2Method(Phase2.GTC);
733                                 break;
734                             default:
735                                 Log.e(TAG, "Unknown phase2 method" + phase2Method);
736                                 break;
737                         }
738                         break;
739                     default:
740                         break;
741                 }
742 
743                 String caCert = (String) mEapCaCertSpinner.getSelectedItem();
744                 config.enterpriseConfig.setCaCertificateAliases(null);
745                 config.enterpriseConfig.setCaPath(null);
746                 config.enterpriseConfig.setDomainSuffixMatch(mEapDomainView.getText().toString());
747                 if (caCert.equals(mUnspecifiedCertString)) {
748                     // ca_cert already set to null, so do nothing.
749                 } else if (caCert.equals(mUseSystemCertsString)) {
750                     config.enterpriseConfig.setCaPath(SYSTEM_CA_STORE_PATH);
751                 } else if (caCert.equals(mMultipleCertSetString)) {
752                     if (mAccessPoint != null) {
753                         if (!mAccessPoint.isSaved()) {
754                             Log.e(TAG, "Multiple certs can only be set "
755                                     + "when editing saved network");
756                         }
757                         config.enterpriseConfig.setCaCertificateAliases(
758                                 mAccessPoint
759                                         .getConfig()
760                                         .enterpriseConfig
761                                         .getCaCertificateAliases());
762                     }
763                 } else {
764                     config.enterpriseConfig.setCaCertificateAliases(new String[] {caCert});
765                 }
766 
767                 // ca_cert or ca_path should not both be non-null, since we only intend to let
768                 // the use either their own certificate, or the system certificates, not both.
769                 // The variable that is not used must explicitly be set to null, so that a
770                 // previously-set value on a saved configuration will be erased on an update.
771                 if (config.enterpriseConfig.getCaCertificateAliases() != null
772                         && config.enterpriseConfig.getCaPath() != null) {
773                     Log.e(TAG, "ca_cert ("
774                             + Arrays.toString(config.enterpriseConfig.getCaCertificateAliases())
775                             + ") and ca_path ("
776                             + config.enterpriseConfig.getCaPath()
777                             + ") should not both be non-null");
778                 }
779 
780                 // Only set OCSP option if there is a valid CA certificate.
781                 if (caCert.equals(mUnspecifiedCertString)) {
782                     config.enterpriseConfig.setOcsp(WifiEnterpriseConfig.OCSP_NONE);
783                 } else {
784                     config.enterpriseConfig.setOcsp(mEapOcspSpinner.getSelectedItemPosition());
785                 }
786 
787                 String clientCert = (String) mEapUserCertSpinner.getSelectedItem();
788                 if (clientCert.equals(mUnspecifiedCertString)
789                         || clientCert.equals(mDoNotProvideEapUserCertString)) {
790                     // Note: |clientCert| should not be able to take the value |unspecifiedCert|,
791                     // since we prevent such configurations from being saved.
792                     clientCert = "";
793                 }
794                 config.enterpriseConfig.setClientCertificateAlias(clientCert);
795                 if (eapMethod == Eap.SIM || eapMethod == Eap.AKA || eapMethod == Eap.AKA_PRIME) {
796                     config.enterpriseConfig.setIdentity("");
797                     config.enterpriseConfig.setAnonymousIdentity("");
798                 } else if (eapMethod == Eap.PWD) {
799                     config.enterpriseConfig.setIdentity(mEapIdentityView.getText().toString());
800                     config.enterpriseConfig.setAnonymousIdentity("");
801                 } else {
802                     config.enterpriseConfig.setIdentity(mEapIdentityView.getText().toString());
803                     config.enterpriseConfig.setAnonymousIdentity(
804                             mEapAnonymousView.getText().toString());
805                 }
806 
807                 if (mPasswordView.isShown()) {
808                     // For security reasons, a previous password is not displayed to user.
809                     // Update only if it has been changed.
810                     if (mPasswordView.length() > 0) {
811                         config.enterpriseConfig.setPassword(mPasswordView.getText().toString());
812                     }
813                 } else {
814                     // clear password
815                     config.enterpriseConfig.setPassword(mPasswordView.getText().toString());
816                 }
817                 break;
818             case AccessPoint.SECURITY_SAE:
819                 config.setSecurityParams(WifiConfiguration.SECURITY_TYPE_SAE);
820                 if (mPasswordView.length() != 0) {
821                     String password = mPasswordView.getText().toString();
822                     config.preSharedKey = '"' + password + '"';
823                 }
824                 break;
825 
826             case AccessPoint.SECURITY_OWE:
827                 config.setSecurityParams(WifiConfiguration.SECURITY_TYPE_OWE);
828                 break;
829 
830             default:
831                 return null;
832         }
833 
834         if (config.enterpriseConfig.isAuthenticationSimBased()
835                 && mActiveSubscriptionInfos.size() > 0) {
836             config.carrierId = mActiveSubscriptionInfos
837                     .get(mEapSimSpinner.getSelectedItemPosition()).getCarrierId();
838         }
839 
840         final IpConfiguration ipConfig = new IpConfiguration();
841         ipConfig.setIpAssignment(mIpAssignment);
842         ipConfig.setProxySettings(mProxySettings);
843         ipConfig.setStaticIpConfiguration(mStaticIpConfiguration);
844         ipConfig.setHttpProxy(mHttpProxy);
845         config.setIpConfiguration(ipConfig);
846         if (mMeteredSettingsSpinner != null) {
847             config.meteredOverride = mMeteredSettingsSpinner.getSelectedItemPosition();
848         }
849 
850         if (mPrivacySettingsSpinner != null) {
851             config.macRandomizationSetting = mPrivacySettingsSpinner.getSelectedItemPosition()
852                     == PRIVACY_SPINNER_INDEX_RANDOMIZED_MAC
853                     ? WifiConfiguration.RANDOMIZATION_PERSISTENT
854                     : WifiConfiguration.RANDOMIZATION_NONE;
855         }
856 
857         if (mDhcpSettingsSpinner != null) {
858             config.setSendDhcpHostnameEnabled(WifiPrivacyPreferenceController.Companion
859                     .translatePrefValueToSendDhcpHostnameEnabled(mDhcpSettingsSpinner
860                             .getSelectedItemPosition()));
861         }
862 
863         return config;
864     }
865 
ipAndProxyFieldsAreValid()866     private boolean ipAndProxyFieldsAreValid() {
867         mIpAssignment =
868                 (mIpSettingsSpinner != null
869                     && mIpSettingsSpinner.getSelectedItemPosition() == STATIC_IP)
870                 ? IpAssignment.STATIC
871                 : IpAssignment.DHCP;
872 
873         if (mIpAssignment == IpAssignment.STATIC) {
874             mStaticIpConfiguration = new StaticIpConfiguration();
875             int result = validateIpConfigFields(mStaticIpConfiguration);
876             if (result != 0) {
877                 return false;
878             }
879         }
880 
881         final int selectedPosition = mProxySettingsSpinner.getSelectedItemPosition();
882         mProxySettings = ProxySettings.NONE;
883         mHttpProxy = null;
884         if (selectedPosition == PROXY_STATIC && mProxyHostView != null) {
885             mProxySettings = ProxySettings.STATIC;
886             String host = mProxyHostView.getText().toString();
887             String portStr = mProxyPortView.getText().toString();
888             String exclusionList = mProxyExclusionListView.getText().toString();
889             int port = 0;
890             int result = 0;
891             try {
892                 port = Integer.parseInt(portStr);
893                 result = ProxySelector.validate(host, portStr, exclusionList);
894             } catch (NumberFormatException e) {
895                 result = R.string.proxy_error_invalid_port;
896             }
897             if (result == 0) {
898                 mHttpProxy = ProxyInfo.buildDirectProxy(
899                         host, port, Arrays.asList(exclusionList.split(",")));
900             } else {
901                 return false;
902             }
903         } else if (selectedPosition == PROXY_PAC && mProxyPacView != null) {
904             mProxySettings = ProxySettings.PAC;
905             CharSequence uriSequence = mProxyPacView.getText();
906             if (TextUtils.isEmpty(uriSequence)) {
907                 return false;
908             }
909             Uri uri = Uri.parse(uriSequence.toString());
910             if (uri == null) {
911                 return false;
912             }
913             mHttpProxy = ProxyInfo.buildPacProxy(uri);
914         }
915         return true;
916     }
917 
getIPv4Address(String text)918     private Inet4Address getIPv4Address(String text) {
919         try {
920             return (Inet4Address) InetAddresses.parseNumericAddress(text);
921         } catch (IllegalArgumentException | ClassCastException e) {
922             return null;
923         }
924     }
925 
validateIpConfigFields(StaticIpConfiguration staticIpConfiguration)926     private int validateIpConfigFields(StaticIpConfiguration staticIpConfiguration) {
927         if (mIpAddressView == null) return 0;
928 
929         String ipAddr = mIpAddressView.getText().toString();
930         if (TextUtils.isEmpty(ipAddr)) return R.string.wifi_ip_settings_invalid_ip_address;
931 
932         Inet4Address inetAddr = getIPv4Address(ipAddr);
933         if (inetAddr == null || inetAddr.equals(Inet4Address.ANY)) {
934             return R.string.wifi_ip_settings_invalid_ip_address;
935         }
936         // Copy all fields into the builder first and set desired value later with builder.
937         final StaticIpConfiguration.Builder staticIPBuilder = new StaticIpConfiguration.Builder()
938                 .setDnsServers(staticIpConfiguration.getDnsServers())
939                 .setDomains(staticIpConfiguration.getDomains())
940                 .setGateway(staticIpConfiguration.getGateway())
941                 .setIpAddress(staticIpConfiguration.getIpAddress());
942         try {
943             int networkPrefixLength = -1;
944             try {
945                 networkPrefixLength = Integer.parseInt(
946                         mNetworkPrefixLengthView.getText().toString());
947                 if (networkPrefixLength < 0 || networkPrefixLength > 32) {
948                     return R.string.wifi_ip_settings_invalid_network_prefix_length;
949                 }
950                 staticIPBuilder.setIpAddress(new LinkAddress(inetAddr, networkPrefixLength));
951             } catch (NumberFormatException e) {
952                 // Set the hint as default after user types in ip address
953                 mNetworkPrefixLengthView.setText(mConfigUi.getContext().getString(
954                         com.android.settingslib.R.string.wifi_network_prefix_length_hint));
955             } catch (IllegalArgumentException e) {
956                 return R.string.wifi_ip_settings_invalid_ip_address;
957             }
958 
959             String gateway = mGatewayView.getText().toString();
960             if (TextUtils.isEmpty(gateway)) {
961                 try {
962                     //Extract a default gateway from IP address
963                     InetAddress netPart = NetUtils.getNetworkPart(inetAddr, networkPrefixLength);
964                     byte[] addr = netPart.getAddress();
965                     addr[addr.length - 1] = 1;
966                     mGatewayView.setText(InetAddress.getByAddress(addr).getHostAddress());
967                 } catch (RuntimeException ee) {
968                 } catch (java.net.UnknownHostException u) {
969                 }
970             } else {
971                 InetAddress gatewayAddr = getIPv4Address(gateway);
972                 if (gatewayAddr == null) {
973                     return R.string.wifi_ip_settings_invalid_gateway;
974                 }
975                 if (gatewayAddr.isMulticastAddress()) {
976                     return R.string.wifi_ip_settings_invalid_gateway;
977                 }
978                 staticIPBuilder.setGateway(gatewayAddr);
979             }
980 
981             String dns = mDns1View.getText().toString();
982             InetAddress dnsAddr = null;
983             final ArrayList<InetAddress> dnsServers = new ArrayList<>();
984 
985             if (TextUtils.isEmpty(dns)) {
986                 //If everything else is valid, provide hint as a default option
987                 mDns1View.setText(mConfigUi.getContext().getString(
988                         com.android.settingslib.R.string.wifi_dns1_hint));
989             } else {
990                 dnsAddr = getIPv4Address(dns);
991                 if (dnsAddr == null) {
992                     return R.string.wifi_ip_settings_invalid_dns;
993                 }
994                 dnsServers.add(dnsAddr);
995             }
996 
997             if (mDns2View.length() > 0) {
998                 dns = mDns2View.getText().toString();
999                 dnsAddr = getIPv4Address(dns);
1000                 if (dnsAddr == null) {
1001                     return R.string.wifi_ip_settings_invalid_dns;
1002                 }
1003                 dnsServers.add(dnsAddr);
1004             }
1005             staticIPBuilder.setDnsServers(dnsServers);
1006             return 0;
1007         } finally {
1008             // Caller of this method may rely on staticIpConfiguration, so build the final result
1009             // at the end of the method.
1010             staticIpConfiguration = staticIPBuilder.build();
1011         }
1012     }
1013 
showSecurityFields(boolean refreshEapMethods, boolean refreshCertificates)1014     private void showSecurityFields(boolean refreshEapMethods, boolean refreshCertificates) {
1015         if (mAccessPointSecurity == AccessPoint.SECURITY_NONE ||
1016                 mAccessPointSecurity == AccessPoint.SECURITY_OWE) {
1017             mView.findViewById(R.id.security_fields).setVisibility(View.GONE);
1018             return;
1019         }
1020         mView.findViewById(R.id.security_fields).setVisibility(View.VISIBLE);
1021 
1022         if (mPasswordView == null) {
1023             mPasswordView = (TextView) mView.findViewById(R.id.password);
1024             mPasswordView.addTextChangedListener(this);
1025             mPasswordView.setOnEditorActionListener(this);
1026             mPasswordView.setOnKeyListener(this);
1027             ((CheckBox) mView.findViewById(R.id.show_password))
1028                 .setOnCheckedChangeListener(this);
1029 
1030             if (mAccessPoint != null && mAccessPoint.isSaved()) {
1031                 mPasswordView.setHint(R.string.wifi_unchanged);
1032             }
1033         }
1034 
1035         if (mAccessPointSecurity != AccessPoint.SECURITY_EAP &&
1036                 mAccessPointSecurity != AccessPoint.SECURITY_EAP_SUITE_B) {
1037             mView.findViewById(R.id.eap).setVisibility(View.GONE);
1038             return;
1039         }
1040         mView.findViewById(R.id.eap).setVisibility(View.VISIBLE);
1041 
1042         // TODO (b/140541213): Maybe we can remove initiateEnterpriseNetworkUi by moving code block
1043         boolean initiateEnterpriseNetworkUi = false;
1044         if (mEapMethodSpinner == null) {
1045             initiateEnterpriseNetworkUi = true;
1046             mEapMethodSpinner = (Spinner) mView.findViewById(R.id.method);
1047             mEapMethodSpinner.setOnItemSelectedListener(this);
1048             mEapSimSpinner = (Spinner) mView.findViewById(R.id.sim);
1049             mPhase2Spinner = (Spinner) mView.findViewById(R.id.phase2);
1050             mPhase2Spinner.setOnItemSelectedListener(this);
1051             mEapCaCertSpinner = (Spinner) mView.findViewById(R.id.ca_cert);
1052             mEapCaCertSpinner.setOnItemSelectedListener(this);
1053             mEapOcspSpinner = (Spinner) mView.findViewById(R.id.ocsp);
1054             mEapDomainView = (TextView) mView.findViewById(R.id.domain);
1055             mEapDomainView.addTextChangedListener(this);
1056             mEapUserCertSpinner = (Spinner) mView.findViewById(R.id.user_cert);
1057             mEapUserCertSpinner.setOnItemSelectedListener(this);
1058             mEapIdentityView = (TextView) mView.findViewById(R.id.identity);
1059             mEapAnonymousView = (TextView) mView.findViewById(R.id.anonymous);
1060 
1061             setAccessibilityDelegateForSecuritySpinners();
1062         }
1063 
1064         if (refreshEapMethods) {
1065             ArrayAdapter<CharSequence> eapMethodSpinnerAdapter;
1066             if (mAccessPointSecurity == AccessPoint.SECURITY_EAP_SUITE_B) {
1067                 eapMethodSpinnerAdapter = getSpinnerAdapter(R.array.wifi_eap_method);
1068                 mEapMethodSpinner.setAdapter(eapMethodSpinnerAdapter);
1069                 // WAP3-Enterprise 192-bit only allows EAP method TLS
1070                 mEapMethodSpinner.setSelection(Eap.TLS);
1071                 mEapMethodSpinner.setEnabled(false);
1072             } else if (Utils.isWifiOnly(mContext) || !mContext.getResources().getBoolean(
1073                     com.android.internal.R.bool.config_eap_sim_based_auth_supported)) {
1074                 eapMethodSpinnerAdapter = getSpinnerAdapter(R.array.eap_method_without_sim_auth);
1075                 mEapMethodSpinner.setAdapter(eapMethodSpinnerAdapter);
1076                 mEapMethodSpinner.setEnabled(true);
1077             } else {
1078                 eapMethodSpinnerAdapter = getSpinnerAdapterWithEapMethodsTts(R.array.wifi_eap_method);
1079                 mEapMethodSpinner.setAdapter(eapMethodSpinnerAdapter);
1080                 mEapMethodSpinner.setEnabled(true);
1081             }
1082         }
1083 
1084         if (refreshCertificates) {
1085             loadSims();
1086 
1087             final AndroidKeystoreAliasLoader androidKeystoreAliasLoader =
1088                     getAndroidKeystoreAliasLoader();
1089             loadCertificates(
1090                     mEapCaCertSpinner,
1091                     androidKeystoreAliasLoader.getCaCertAliases(),
1092                     null /* noCertificateString */,
1093                     false /* showMultipleCerts */,
1094                     true /* showUsePreinstalledCertOption */);
1095             loadCertificates(
1096                     mEapUserCertSpinner,
1097                     androidKeystoreAliasLoader.getKeyCertAliases(),
1098                     mDoNotProvideEapUserCertString,
1099                     false /* showMultipleCerts */,
1100                     false /* showUsePreinstalledCertOption */);
1101             // To avoid the user connects to a non-secure network unexpectedly,
1102             // request using system trusted certificates by default
1103             // unless the user explicitly chooses "Do not validate" or other
1104             // CA certificates.
1105             setSelection(mEapCaCertSpinner, mUseSystemCertsString);
1106         }
1107 
1108         // Modifying an existing network
1109         if (initiateEnterpriseNetworkUi && mAccessPoint != null && mAccessPoint.isSaved()) {
1110             final WifiConfiguration wifiConfig = mAccessPoint.getConfig();
1111             final WifiEnterpriseConfig enterpriseConfig = wifiConfig.enterpriseConfig;
1112             final int eapMethod = enterpriseConfig.getEapMethod();
1113             final int phase2Method = enterpriseConfig.getPhase2Method();
1114             mEapMethodSpinner.setSelection(eapMethod);
1115             showEapFieldsByMethod(eapMethod);
1116             switch (eapMethod) {
1117                 case Eap.PEAP:
1118                     switch (phase2Method) {
1119                         case Phase2.MSCHAPV2:
1120                             mPhase2Spinner.setSelection(WIFI_PEAP_PHASE2_MSCHAPV2);
1121                             break;
1122                         case Phase2.GTC:
1123                             mPhase2Spinner.setSelection(WIFI_PEAP_PHASE2_GTC);
1124                             break;
1125                         case Phase2.SIM:
1126                             mPhase2Spinner.setSelection(WIFI_PEAP_PHASE2_SIM);
1127                             break;
1128                         case Phase2.AKA:
1129                             mPhase2Spinner.setSelection(WIFI_PEAP_PHASE2_AKA);
1130                             break;
1131                         case Phase2.AKA_PRIME:
1132                             mPhase2Spinner.setSelection(WIFI_PEAP_PHASE2_AKA_PRIME);
1133                             break;
1134                         default:
1135                             Log.e(TAG, "Invalid phase 2 method " + phase2Method);
1136                             break;
1137                     }
1138                     break;
1139                 case Eap.TTLS:
1140                     switch (phase2Method) {
1141                         case Phase2.PAP:
1142                             mPhase2Spinner.setSelection(WIFI_TTLS_PHASE2_PAP);
1143                             break;
1144                         case Phase2.MSCHAP:
1145                             mPhase2Spinner.setSelection(WIFI_TTLS_PHASE2_MSCHAP);
1146                             break;
1147                         case Phase2.MSCHAPV2:
1148                             mPhase2Spinner.setSelection(WIFI_TTLS_PHASE2_MSCHAPV2);
1149                             break;
1150                         case Phase2.GTC:
1151                             mPhase2Spinner.setSelection(WIFI_TTLS_PHASE2_GTC);
1152                             break;
1153                         default:
1154                             Log.e(TAG, "Invalid phase 2 method " + phase2Method);
1155                             break;
1156                     }
1157                     break;
1158                 default:
1159                     break;
1160             }
1161 
1162             if (enterpriseConfig.isAuthenticationSimBased()) {
1163                 for (int i = 0; i < mActiveSubscriptionInfos.size(); i++) {
1164                     if (wifiConfig.carrierId == mActiveSubscriptionInfos.get(i).getCarrierId()) {
1165                         mEapSimSpinner.setSelection(i);
1166                         break;
1167                     }
1168                 }
1169             }
1170 
1171             if (!TextUtils.isEmpty(enterpriseConfig.getCaPath())) {
1172                 setSelection(mEapCaCertSpinner, mUseSystemCertsString);
1173             } else {
1174                 String[] caCerts = enterpriseConfig.getCaCertificateAliases();
1175                 if (caCerts == null) {
1176                     setSelection(mEapCaCertSpinner, mUnspecifiedCertString);
1177                 } else if (caCerts.length == 1) {
1178                     setSelection(mEapCaCertSpinner, caCerts[0]);
1179                 } else {
1180                     final AndroidKeystoreAliasLoader androidKeystoreAliasLoader =
1181                             getAndroidKeystoreAliasLoader();
1182 
1183                     // Reload the cert spinner with an extra "multiple certificates added" item.
1184                     loadCertificates(
1185                             mEapCaCertSpinner,
1186                             androidKeystoreAliasLoader.getCaCertAliases(),
1187                             null /* noCertificateString */,
1188                             true /* showMultipleCerts */,
1189                             true /* showUsePreinstalledCertOption */);
1190                     setSelection(mEapCaCertSpinner, mMultipleCertSetString);
1191                 }
1192             }
1193             mEapOcspSpinner.setSelection(enterpriseConfig.getOcsp());
1194             mEapDomainView.setText(enterpriseConfig.getDomainSuffixMatch());
1195             String userCert = enterpriseConfig.getClientCertificateAlias();
1196             if (TextUtils.isEmpty(userCert)) {
1197                 setSelection(mEapUserCertSpinner, mDoNotProvideEapUserCertString);
1198             } else {
1199                 setSelection(mEapUserCertSpinner, userCert);
1200             }
1201             mEapIdentityView.setText(enterpriseConfig.getIdentity());
1202             mEapAnonymousView.setText(enterpriseConfig.getAnonymousIdentity());
1203         } else {
1204             showEapFieldsByMethod(mEapMethodSpinner.getSelectedItemPosition());
1205         }
1206     }
1207 
setAccessibilityDelegateForSecuritySpinners()1208     private void setAccessibilityDelegateForSecuritySpinners() {
1209         final AccessibilityDelegate selectedEventBlocker = new AccessibilityDelegate() {
1210             @Override
1211             public void sendAccessibilityEvent(View host, int eventType) {
1212                 if (eventType == AccessibilityEvent.TYPE_VIEW_SELECTED) {
1213                     // Ignore TYPE_VIEW_SELECTED or there will be multiple Spinner selected
1214                     // information for WifiController#showSecurityFields.
1215                     return;
1216                 }
1217                 super.sendAccessibilityEvent(host, eventType);
1218             }
1219         };
1220 
1221         mEapMethodSpinner.setAccessibilityDelegate(selectedEventBlocker);
1222         mPhase2Spinner.setAccessibilityDelegate(selectedEventBlocker);
1223         mEapCaCertSpinner.setAccessibilityDelegate(selectedEventBlocker);
1224         mEapOcspSpinner.setAccessibilityDelegate(selectedEventBlocker);
1225         mEapUserCertSpinner.setAccessibilityDelegate(selectedEventBlocker);
1226     }
1227 
1228     /**
1229      * EAP-PWD valid fields include
1230      *   identity
1231      *   password
1232      * EAP-PEAP valid fields include
1233      *   phase2: MSCHAPV2, GTC, SIM, AKA, AKA'
1234      *   ca_cert
1235      *   identity
1236      *   anonymous_identity
1237      *   password (not required for SIM, AKA, AKA')
1238      * EAP-TLS valid fields include
1239      *   user_cert
1240      *   ca_cert
1241      *   domain
1242      *   identity
1243      * EAP-TTLS valid fields include
1244      *   phase2: PAP, MSCHAP, MSCHAPV2, GTC
1245      *   ca_cert
1246      *   identity
1247      *   anonymous_identity
1248      *   password
1249      */
showEapFieldsByMethod(int eapMethod)1250     private void showEapFieldsByMethod(int eapMethod) {
1251         // Common defaults
1252         mView.findViewById(R.id.l_method).setVisibility(View.VISIBLE);
1253         mView.findViewById(R.id.l_identity).setVisibility(View.VISIBLE);
1254         mView.findViewById(R.id.l_domain).setVisibility(View.VISIBLE);
1255 
1256         // Defaults for most of the EAP methods and over-riden by
1257         // by certain EAP methods
1258         mView.findViewById(R.id.l_ca_cert).setVisibility(View.VISIBLE);
1259         mView.findViewById(R.id.l_ocsp).setVisibility(View.VISIBLE);
1260         mView.findViewById(R.id.password_layout).setVisibility(View.VISIBLE);
1261         mView.findViewById(R.id.show_password_layout).setVisibility(View.VISIBLE);
1262         mView.findViewById(R.id.l_sim).setVisibility(View.VISIBLE);
1263 
1264         Context context = mConfigUi.getContext();
1265         switch (eapMethod) {
1266             case WIFI_EAP_METHOD_PWD:
1267                 setPhase2Invisible();
1268                 setCaCertInvisible();
1269                 setOcspInvisible();
1270                 setDomainInvisible();
1271                 setAnonymousIdentInvisible();
1272                 setUserCertInvisible();
1273                 mView.findViewById(R.id.l_sim).setVisibility(View.GONE);
1274                 break;
1275             case WIFI_EAP_METHOD_TLS:
1276                 mView.findViewById(R.id.l_user_cert).setVisibility(View.VISIBLE);
1277                 setPhase2Invisible();
1278                 setAnonymousIdentInvisible();
1279                 setPasswordInvisible();
1280                 mView.findViewById(R.id.l_sim).setVisibility(View.GONE);
1281                 break;
1282             case WIFI_EAP_METHOD_PEAP:
1283                 // Reset adapter if needed
1284                 if (mPhase2Adapter != mPhase2PeapAdapter) {
1285                     mPhase2Adapter = mPhase2PeapAdapter;
1286                     mPhase2Spinner.setAdapter(mPhase2Adapter);
1287                 }
1288                 mView.findViewById(R.id.l_phase2).setVisibility(View.VISIBLE);
1289                 mView.findViewById(R.id.l_anonymous).setVisibility(View.VISIBLE);
1290                 showPeapFields();
1291                 setUserCertInvisible();
1292                 break;
1293             case WIFI_EAP_METHOD_TTLS:
1294                 // Reset adapter if needed
1295                 if (mPhase2Adapter != mPhase2TtlsAdapter) {
1296                     mPhase2Adapter = mPhase2TtlsAdapter;
1297                     mPhase2Spinner.setAdapter(mPhase2Adapter);
1298                 }
1299                 mView.findViewById(R.id.l_phase2).setVisibility(View.VISIBLE);
1300                 mView.findViewById(R.id.l_anonymous).setVisibility(View.VISIBLE);
1301                 setUserCertInvisible();
1302                 mView.findViewById(R.id.l_sim).setVisibility(View.GONE);
1303                 break;
1304             case WIFI_EAP_METHOD_SIM:
1305             case WIFI_EAP_METHOD_AKA:
1306             case WIFI_EAP_METHOD_AKA_PRIME:
1307                 setPhase2Invisible();
1308                 setAnonymousIdentInvisible();
1309                 setCaCertInvisible();
1310                 setOcspInvisible();
1311                 setDomainInvisible();
1312                 setUserCertInvisible();
1313                 setPasswordInvisible();
1314                 setIdentityInvisible();
1315                 break;
1316         }
1317 
1318         if (mView.findViewById(R.id.l_ca_cert).getVisibility() != View.GONE) {
1319             String eapCertSelection = (String) mEapCaCertSpinner.getSelectedItem();
1320             if (eapCertSelection.equals(mUnspecifiedCertString)) {
1321                 // Domain suffix matching is not relevant if the user hasn't chosen a CA
1322                 // certificate yet, or chooses not to validate the EAP server.
1323                 setDomainInvisible();
1324                 // Ocsp is an additional validation step for a server certifidate.
1325                 // This field is not relevant if the user hasn't chosen a valid
1326                 // CA certificate yet.
1327                 setOcspInvisible();
1328             }
1329         }
1330     }
1331 
showPeapFields()1332     private void showPeapFields() {
1333         int phase2Method = mPhase2Spinner.getSelectedItemPosition();
1334         if (phase2Method == WIFI_PEAP_PHASE2_SIM || phase2Method == WIFI_PEAP_PHASE2_AKA
1335                  || phase2Method == WIFI_PEAP_PHASE2_AKA_PRIME) {
1336             mEapIdentityView.setText("");
1337             mView.findViewById(R.id.l_identity).setVisibility(View.GONE);
1338             setPasswordInvisible();
1339             mView.findViewById(R.id.l_sim).setVisibility(View.VISIBLE);
1340         } else {
1341             mView.findViewById(R.id.l_identity).setVisibility(View.VISIBLE);
1342             mView.findViewById(R.id.l_anonymous).setVisibility(View.VISIBLE);
1343             mView.findViewById(R.id.password_layout).setVisibility(View.VISIBLE);
1344             mView.findViewById(R.id.show_password_layout).setVisibility(View.VISIBLE);
1345             mView.findViewById(R.id.l_sim).setVisibility(View.GONE);
1346         }
1347     }
1348 
setIdentityInvisible()1349     private void setIdentityInvisible() {
1350         mView.findViewById(R.id.l_identity).setVisibility(View.GONE);
1351     }
1352 
setPhase2Invisible()1353     private void setPhase2Invisible() {
1354         mView.findViewById(R.id.l_phase2).setVisibility(View.GONE);
1355     }
1356 
setCaCertInvisible()1357     private void setCaCertInvisible() {
1358         mView.findViewById(R.id.l_ca_cert).setVisibility(View.GONE);
1359         setSelection(mEapCaCertSpinner, mUnspecifiedCertString);
1360     }
1361 
setOcspInvisible()1362     private void setOcspInvisible() {
1363         mView.findViewById(R.id.l_ocsp).setVisibility(View.GONE);
1364         mEapOcspSpinner.setSelection(WifiEnterpriseConfig.OCSP_NONE);
1365     }
1366 
setDomainInvisible()1367     private void setDomainInvisible() {
1368         mView.findViewById(R.id.l_domain).setVisibility(View.GONE);
1369         mEapDomainView.setText("");
1370     }
1371 
setUserCertInvisible()1372     private void setUserCertInvisible() {
1373         mView.findViewById(R.id.l_user_cert).setVisibility(View.GONE);
1374         setSelection(mEapUserCertSpinner, mUnspecifiedCertString);
1375     }
1376 
setAnonymousIdentInvisible()1377     private void setAnonymousIdentInvisible() {
1378         mView.findViewById(R.id.l_anonymous).setVisibility(View.GONE);
1379         mEapAnonymousView.setText("");
1380     }
1381 
setPasswordInvisible()1382     private void setPasswordInvisible() {
1383         mPasswordView.setText("");
1384         mView.findViewById(R.id.password_layout).setVisibility(View.GONE);
1385         mView.findViewById(R.id.show_password_layout).setVisibility(View.GONE);
1386     }
1387 
setEapMethodInvisible()1388     private void setEapMethodInvisible() {
1389         mView.findViewById(R.id.eap).setVisibility(View.GONE);
1390     }
1391 
showIpConfigFields()1392     private void showIpConfigFields() {
1393         WifiConfiguration config = null;
1394 
1395         mView.findViewById(R.id.ip_fields).setVisibility(View.VISIBLE);
1396 
1397         if (mAccessPoint != null && mAccessPoint.isSaved()) {
1398             config = mAccessPoint.getConfig();
1399         }
1400 
1401         if (mIpSettingsSpinner.getSelectedItemPosition() == STATIC_IP) {
1402             mView.findViewById(R.id.staticip).setVisibility(View.VISIBLE);
1403             if (mIpAddressView == null) {
1404                 mIpAddressView = (TextView) mView.findViewById(R.id.ipaddress);
1405                 mIpAddressView.addTextChangedListener(this);
1406                 mGatewayView = (TextView) mView.findViewById(R.id.gateway);
1407                 mGatewayView.addTextChangedListener(this);
1408                 mNetworkPrefixLengthView = (TextView) mView.findViewById(
1409                         R.id.network_prefix_length);
1410                 mNetworkPrefixLengthView.addTextChangedListener(this);
1411                 mDns1View = (TextView) mView.findViewById(R.id.dns1);
1412                 mDns1View.addTextChangedListener(this);
1413                 mDns2View = (TextView) mView.findViewById(R.id.dns2);
1414                 mDns2View.addTextChangedListener(this);
1415             }
1416             if (config != null) {
1417                 StaticIpConfiguration staticConfig = config.getIpConfiguration()
1418                         .getStaticIpConfiguration();
1419                 if (staticConfig != null) {
1420                     if (staticConfig.getIpAddress() != null) {
1421                         mIpAddressView.setText(
1422                                 staticConfig.getIpAddress().getAddress().getHostAddress());
1423                         mNetworkPrefixLengthView.setText(Integer.toString(
1424                                 staticConfig.getIpAddress().getPrefixLength()));
1425                     }
1426 
1427                     if (staticConfig.getGateway() != null) {
1428                         mGatewayView.setText(staticConfig.getGateway().getHostAddress());
1429                     }
1430 
1431                     Iterator<InetAddress> dnsIterator = staticConfig.getDnsServers().iterator();
1432                     if (dnsIterator.hasNext()) {
1433                         mDns1View.setText(dnsIterator.next().getHostAddress());
1434                     }
1435                     if (dnsIterator.hasNext()) {
1436                         mDns2View.setText(dnsIterator.next().getHostAddress());
1437                     }
1438                 }
1439             }
1440         } else {
1441             mView.findViewById(R.id.staticip).setVisibility(View.GONE);
1442         }
1443     }
1444 
showProxyFields()1445     private void showProxyFields() {
1446         WifiConfiguration config = null;
1447 
1448         mView.findViewById(R.id.proxy_settings_fields).setVisibility(View.VISIBLE);
1449 
1450         if (mAccessPoint != null && mAccessPoint.isSaved()) {
1451             config = mAccessPoint.getConfig();
1452         }
1453 
1454         if (mProxySettingsSpinner.getSelectedItemPosition() == PROXY_STATIC) {
1455             setVisibility(R.id.proxy_warning_limited_support, View.VISIBLE);
1456             setVisibility(R.id.proxy_fields, View.VISIBLE);
1457             setVisibility(R.id.proxy_pac_field, View.GONE);
1458             if (mProxyHostView == null) {
1459                 mProxyHostView = (TextView) mView.findViewById(R.id.proxy_hostname);
1460                 mProxyHostView.addTextChangedListener(this);
1461                 mProxyPortView = (TextView) mView.findViewById(R.id.proxy_port);
1462                 mProxyPortView.addTextChangedListener(this);
1463                 mProxyExclusionListView = (TextView) mView.findViewById(R.id.proxy_exclusionlist);
1464                 mProxyExclusionListView.addTextChangedListener(this);
1465             }
1466             if (config != null) {
1467                 ProxyInfo proxyProperties = config.getHttpProxy();
1468                 if (proxyProperties != null) {
1469                     mProxyHostView.setText(proxyProperties.getHost());
1470                     mProxyPortView.setText(Integer.toString(proxyProperties.getPort()));
1471                     mProxyExclusionListView.setText(
1472                             ProxyUtils.exclusionListAsString(proxyProperties.getExclusionList()));
1473                 }
1474             }
1475         } else if (mProxySettingsSpinner.getSelectedItemPosition() == PROXY_PAC) {
1476             setVisibility(R.id.proxy_warning_limited_support, View.GONE);
1477             setVisibility(R.id.proxy_fields, View.GONE);
1478             setVisibility(R.id.proxy_pac_field, View.VISIBLE);
1479 
1480             if (mProxyPacView == null) {
1481                 mProxyPacView = (TextView) mView.findViewById(R.id.proxy_pac);
1482                 mProxyPacView.addTextChangedListener(this);
1483             }
1484             if (config != null) {
1485                 ProxyInfo proxyInfo = config.getHttpProxy();
1486                 if (proxyInfo != null) {
1487                     mProxyPacView.setText(proxyInfo.getPacFileUrl().toString());
1488                 }
1489             }
1490         } else {
1491             setVisibility(R.id.proxy_warning_limited_support, View.GONE);
1492             setVisibility(R.id.proxy_fields, View.GONE);
1493             setVisibility(R.id.proxy_pac_field, View.GONE);
1494         }
1495     }
1496 
setVisibility(int id, int visibility)1497     private void setVisibility(int id, int visibility) {
1498         final View v = mView.findViewById(id);
1499         if (v != null) {
1500             v.setVisibility(visibility);
1501         }
1502     }
1503 
1504     @VisibleForTesting
getAndroidKeystoreAliasLoader()1505     AndroidKeystoreAliasLoader getAndroidKeystoreAliasLoader() {
1506         return new AndroidKeystoreAliasLoader(KeyProperties.NAMESPACE_WIFI);
1507     }
1508 
1509     @VisibleForTesting
loadSims()1510     void loadSims() {
1511         List<SubscriptionInfo> activeSubscriptionInfos = mContext
1512                 .getSystemService(SubscriptionManager.class).getActiveSubscriptionInfoList();
1513         if (activeSubscriptionInfos == null) {
1514             activeSubscriptionInfos = Collections.EMPTY_LIST;
1515         }
1516         mActiveSubscriptionInfos.clear();
1517 
1518         // De-duplicates active subscriptions and caches in mActiveSubscriptionInfos.
1519         for (SubscriptionInfo newInfo : activeSubscriptionInfos) {
1520             for (SubscriptionInfo cachedInfo : mActiveSubscriptionInfos) {
1521                 if (newInfo.getCarrierId() == cachedInfo.getCarrierId()) {
1522                     continue;
1523                 }
1524             }
1525             mActiveSubscriptionInfos.add(newInfo);
1526         }
1527 
1528         // Shows disabled 'No SIM' when there is no active subscription.
1529         if (mActiveSubscriptionInfos.size() == 0) {
1530             final String[] noSim = new String[]{mContext.getString(R.string.wifi_no_sim_card)};
1531             mEapSimSpinner.setAdapter(getSpinnerAdapter(noSim));
1532             mEapSimSpinner.setSelection(0 /* position */);
1533             mEapSimSpinner.setEnabled(false);
1534             return;
1535         }
1536 
1537         // Shows display name of each active subscription.
1538         final ArrayList<CharSequence> displayNames = new ArrayList<>();
1539         for (SubscriptionInfo activeSubInfo : mActiveSubscriptionInfos) {
1540             displayNames.add(
1541                 SubscriptionUtil.getUniqueSubscriptionDisplayName(activeSubInfo, mContext));
1542         }
1543         mEapSimSpinner.setAdapter(
1544             getSpinnerAdapter(displayNames.toArray(new String[displayNames.size()])));
1545         mEapSimSpinner.setSelection(0 /* position */);
1546         if (displayNames.size() == 1) {
1547             mEapSimSpinner.setEnabled(false);
1548         }
1549     }
1550 
1551     @VisibleForTesting
loadCertificates( Spinner spinner, Collection<String> choices, String noCertificateString, boolean showMultipleCerts, boolean showUsePreinstalledCertOption)1552     void loadCertificates(
1553             Spinner spinner,
1554             Collection<String> choices,
1555             String noCertificateString,
1556             boolean showMultipleCerts,
1557             boolean showUsePreinstalledCertOption) {
1558         final Context context = mConfigUi.getContext();
1559 
1560         ArrayList<String> certs = new ArrayList<String>();
1561         certs.add(mUnspecifiedCertString);
1562         if (showMultipleCerts) {
1563             certs.add(mMultipleCertSetString);
1564         }
1565         if (showUsePreinstalledCertOption) {
1566             certs.add(mUseSystemCertsString);
1567         }
1568 
1569         if (choices != null && choices.size() != 0) {
1570             certs.addAll(choices.stream()
1571                     .filter(certificateName -> {
1572                         for (String undesired : UNDESIRED_CERTIFICATES) {
1573                             if (certificateName.startsWith(undesired)) {
1574                                 return false;
1575                             }
1576                         }
1577                         return true;
1578                     }).collect(Collectors.toList()));
1579         }
1580 
1581         if (!TextUtils.isEmpty(noCertificateString)
1582                 && mAccessPointSecurity != AccessPoint.SECURITY_EAP_SUITE_B) {
1583             certs.add(noCertificateString);
1584         }
1585 
1586         // If there is only mUnspecifiedCertString and one item to select, only shows the item
1587         if (certs.size() == 2) {
1588             certs.remove(mUnspecifiedCertString);
1589             spinner.setEnabled(false);
1590         } else {
1591             spinner.setEnabled(true);
1592         }
1593 
1594         final ArrayAdapter<CharSequence> adapter = getSpinnerAdapter(
1595                 certs.toArray(new String[certs.size()]));
1596         spinner.setAdapter(adapter);
1597     }
1598 
setSelection(Spinner spinner, String value)1599     private void setSelection(Spinner spinner, String value) {
1600         if (value != null) {
1601             @SuppressWarnings("unchecked")
1602             ArrayAdapter<String> adapter = (ArrayAdapter<String>) spinner.getAdapter();
1603             for (int i = adapter.getCount() - 1; i >= 0; --i) {
1604                 if (value.equals(adapter.getItem(i))) {
1605                     spinner.setSelection(i);
1606                     break;
1607                 }
1608             }
1609         }
1610     }
1611 
getMode()1612     public int getMode() {
1613         return mMode;
1614     }
1615 
1616     @Override
afterTextChanged(Editable s)1617     public void afterTextChanged(Editable s) {
1618         ThreadUtils.postOnMainThread(() -> {
1619             showWarningMessagesIfAppropriate();
1620             enableSubmitIfAppropriate();
1621         });
1622     }
1623 
1624     @Override
beforeTextChanged(CharSequence s, int start, int count, int after)1625     public void beforeTextChanged(CharSequence s, int start, int count, int after) {
1626         // work done in afterTextChanged
1627     }
1628 
1629     @Override
onTextChanged(CharSequence s, int start, int before, int count)1630     public void onTextChanged(CharSequence s, int start, int before, int count) {
1631         // work done in afterTextChanged
1632     }
1633 
1634     @Override
onEditorAction(TextView textView, int id, KeyEvent keyEvent)1635     public boolean onEditorAction(TextView textView, int id, KeyEvent keyEvent) {
1636         if (textView == mPasswordView) {
1637             if (id == EditorInfo.IME_ACTION_DONE && isSubmittable()) {
1638                 mConfigUi.dispatchSubmit();
1639                 return true;
1640             }
1641         }
1642         return false;
1643     }
1644 
1645     @Override
onKey(View view, int keyCode, KeyEvent keyEvent)1646     public boolean onKey(View view, int keyCode, KeyEvent keyEvent) {
1647         if (view == mPasswordView) {
1648             if (keyCode == KeyEvent.KEYCODE_ENTER && isSubmittable()) {
1649                 mConfigUi.dispatchSubmit();
1650                 return true;
1651             }
1652         }
1653         return false;
1654     }
1655 
1656     @Override
onCheckedChanged(CompoundButton view, boolean isChecked)1657     public void onCheckedChanged(CompoundButton view, boolean isChecked) {
1658         if (view.getId() == R.id.show_password) {
1659             int pos = mPasswordView.getSelectionEnd();
1660             mPasswordView.setInputType(InputType.TYPE_CLASS_TEXT
1661                     | (isChecked ? InputType.TYPE_TEXT_VARIATION_VISIBLE_PASSWORD
1662                                  : InputType.TYPE_TEXT_VARIATION_PASSWORD));
1663             if (pos >= 0) {
1664                 ((EditText) mPasswordView).setSelection(pos);
1665             }
1666         } else if (view.getId() == R.id.wifi_advanced_togglebox) {
1667             // Hide the SoftKeyboard temporary to let user can see most of the expanded items.
1668             hideSoftKeyboard(mView.getWindowToken());
1669             view.setVisibility(View.GONE);
1670             mView.findViewById(R.id.wifi_advanced_fields).setVisibility(View.VISIBLE);
1671         }
1672     }
1673 
1674     @Override
onItemSelected(AdapterView<?> parent, View view, int position, long id)1675     public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
1676         if (parent == mSecuritySpinner) {
1677             // Convert menu position to actual Wi-Fi security type
1678             mAccessPointSecurity = mSecurityInPosition[position];
1679             showSecurityFields(/* refreshEapMethods */ true, /* refreshCertificates */ true);
1680 
1681             if (WifiDppUtils.isSupportEnrolleeQrCodeScanner(mContext, mAccessPointSecurity)) {
1682                 mSsidScanButton.setVisibility(View.VISIBLE);
1683             } else {
1684                 mSsidScanButton.setVisibility(View.GONE);
1685             }
1686         } else if (parent == mEapMethodSpinner) {
1687             showSecurityFields(/* refreshEapMethods */ false, /* refreshCertificates */ true);
1688         } else if (parent == mEapCaCertSpinner) {
1689             showSecurityFields(/* refreshEapMethods */ false, /* refreshCertificates */ false);
1690         } else if (parent == mPhase2Spinner
1691                 && mEapMethodSpinner.getSelectedItemPosition() == WIFI_EAP_METHOD_PEAP) {
1692             showPeapFields();
1693         } else if (parent == mProxySettingsSpinner) {
1694             showProxyFields();
1695         } else if (parent == mHiddenSettingsSpinner) {
1696             mHiddenWarningView.setVisibility(position == NOT_HIDDEN_NETWORK
1697                     ? View.GONE : View.VISIBLE);
1698         } else {
1699             showIpConfigFields();
1700         }
1701         showWarningMessagesIfAppropriate();
1702         enableSubmitIfAppropriate();
1703     }
1704 
1705     @Override
onNothingSelected(AdapterView<?> parent)1706     public void onNothingSelected(AdapterView<?> parent) {
1707         //
1708     }
1709 
1710     /**
1711      * Make the characters of the password visible if show_password is checked.
1712      */
updatePassword()1713     public void updatePassword() {
1714         TextView passwdView = (TextView) mView.findViewById(R.id.password);
1715         passwdView.setInputType(InputType.TYPE_CLASS_TEXT
1716                 | (((CheckBox) mView.findViewById(R.id.show_password)).isChecked()
1717                    ? InputType.TYPE_TEXT_VARIATION_VISIBLE_PASSWORD
1718                    : InputType.TYPE_TEXT_VARIATION_PASSWORD));
1719     }
1720 
getAccessPoint()1721     public AccessPoint getAccessPoint() {
1722         return mAccessPoint;
1723     }
1724 
configureSecuritySpinner()1725     private void configureSecuritySpinner() {
1726         mConfigUi.setTitle(R.string.wifi_add_network);
1727 
1728         mSsidView = (TextView) mView.findViewById(R.id.ssid);
1729         mSsidView.addTextChangedListener(this);
1730         mSecuritySpinner = ((Spinner) mView.findViewById(R.id.security));
1731         mSecuritySpinner.setOnItemSelectedListener(this);
1732 
1733         ArrayAdapter<String> spinnerAdapter = new ArrayAdapter<String>(mContext,
1734                 android.R.layout.simple_spinner_item, android.R.id.text1);
1735         spinnerAdapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
1736         mSecuritySpinner.setAdapter(spinnerAdapter);
1737         int idx = 0;
1738 
1739         // Populate the Wi-Fi security spinner with the various supported key management types
1740         spinnerAdapter.add(mContext.getString(com.android.settingslib.R.string.wifi_security_none));
1741         mSecurityInPosition[idx++] = AccessPoint.SECURITY_NONE;
1742         if (mWifiManager.isEnhancedOpenSupported()) {
1743             spinnerAdapter.add(
1744                     mContext.getString(com.android.settingslib.R.string.wifi_security_owe));
1745             mSecurityInPosition[idx++] = AccessPoint.SECURITY_OWE;
1746         }
1747         spinnerAdapter.add(mContext.getString(com.android.settingslib.R.string.wifi_security_wep));
1748         mSecurityInPosition[idx++] = AccessPoint.SECURITY_WEP;
1749         spinnerAdapter.add(
1750                 mContext.getString(com.android.settingslib.R.string.wifi_security_wpa_wpa2));
1751         mSecurityInPosition[idx++] = AccessPoint.SECURITY_PSK;
1752         if (mWifiManager.isWpa3SaeSupported()) {
1753             spinnerAdapter.add(
1754                     mContext.getString(com.android.settingslib.R.string.wifi_security_sae));
1755             mSecurityInPosition[idx++] = AccessPoint.SECURITY_SAE;
1756             spinnerAdapter.add(mContext.getString(
1757                     com.android.settingslib.R.string.wifi_security_eap_wpa_wpa2));
1758             mSecurityInPosition[idx++] = AccessPoint.SECURITY_EAP;
1759             spinnerAdapter.add(
1760                     mContext.getString(com.android.settingslib.R.string.wifi_security_eap_wpa3));
1761             mSecurityInPosition[idx++] = AccessPoint.SECURITY_EAP_WPA3_ENTERPRISE;
1762         } else {
1763             spinnerAdapter.add(
1764                     mContext.getString(com.android.settingslib.R.string.wifi_security_eap));
1765             mSecurityInPosition[idx++] = AccessPoint.SECURITY_EAP;
1766         }
1767         if (mWifiManager.isWpa3SuiteBSupported()) {
1768             spinnerAdapter.add(
1769                     mContext.getString(com.android.settingslib.R.string.wifi_security_eap_suiteb));
1770             mSecurityInPosition[idx++] = AccessPoint.SECURITY_EAP_SUITE_B;
1771         }
1772 
1773         spinnerAdapter.notifyDataSetChanged();
1774 
1775         mView.findViewById(R.id.type).setVisibility(View.VISIBLE);
1776 
1777         showIpConfigFields();
1778         showProxyFields();
1779         mView.findViewById(R.id.wifi_advanced_toggle).setVisibility(View.VISIBLE);
1780         // Hidden option can be changed only when the user adds a network manually.
1781         mView.findViewById(R.id.hidden_settings_field).setVisibility(View.VISIBLE);
1782         ((CheckBox) mView.findViewById(R.id.wifi_advanced_togglebox))
1783                 .setOnCheckedChangeListener(this);
1784         // Set correct accessibility strings.
1785         setAdvancedOptionAccessibilityString();
1786     }
1787 
1788     /**
1789      * For each target string in {@code targetStringArray} try to find if it appears in {@code
1790      * originalStringArray}, if found then use the corresponding string, which have the same index
1791      * of the target string in {@code replacementStringArray}, to replace it. And finally return the
1792      * whole new string array back to caller.
1793      */
1794     @VisibleForTesting
findAndReplaceTargetStrings(CharSequence originalStringArray[], CharSequence targetStringArray[], CharSequence replacementStringArray[])1795     CharSequence[] findAndReplaceTargetStrings(CharSequence originalStringArray[],
1796             CharSequence targetStringArray[], CharSequence replacementStringArray[]) {
1797         // The length of the targetStringArray and replacementStringArray should be the same, each
1798         // item in the targetStringArray should have a 1:1 mapping to replacementStringArray, so
1799         // just return the original string if the lengths are different.
1800         if (targetStringArray.length != replacementStringArray.length) {
1801             return originalStringArray;
1802         }
1803 
1804         final CharSequence[] returnEntries = new CharSequence[originalStringArray.length];
1805         for (int i = 0; i < originalStringArray.length; i++) {
1806             returnEntries[i] = originalStringArray[i];
1807             for (int j = 0; j < targetStringArray.length; j++) {
1808                 if (TextUtils.equals(originalStringArray[i], targetStringArray[j])) {
1809                     returnEntries[i] = replacementStringArray[j];
1810                 }
1811             }
1812         }
1813         return returnEntries;
1814     }
1815 
getSpinnerAdapter( int contentStringArrayResId)1816     private ArrayAdapter<CharSequence> getSpinnerAdapter(
1817             int contentStringArrayResId) {
1818         return getSpinnerAdapter(
1819                 mContext.getResources().getStringArray(contentStringArrayResId));
1820     }
1821 
1822     @VisibleForTesting
getSpinnerAdapter( String[] contentStringArray)1823     ArrayAdapter<CharSequence> getSpinnerAdapter(
1824             String[] contentStringArray) {
1825         ArrayAdapter<CharSequence> spinnerAdapter = new ArrayAdapter<>(mContext,
1826                 android.R.layout.simple_spinner_item, contentStringArray);
1827         spinnerAdapter.setDropDownViewResource(
1828                 android.R.layout.simple_spinner_dropdown_item);
1829         return spinnerAdapter;
1830     }
1831 
1832     /**
1833      * This function is to span the TTS strings to each EAP method items in the
1834      * spinner to have detail TTS content for the TTS engine usage.
1835      */
getSpinnerAdapterWithEapMethodsTts( int contentStringArrayResId)1836     private ArrayAdapter<CharSequence> getSpinnerAdapterWithEapMethodsTts(
1837                 int contentStringArrayResId) {
1838         final Resources res = mContext.getResources();
1839         CharSequence[] sourceStrings = res.getStringArray(
1840                 contentStringArrayResId);
1841         CharSequence[] targetStrings = res.getStringArray(
1842                 R.array.wifi_eap_method_target_strings);
1843         CharSequence[] ttsStrings = res.getStringArray(
1844                 R.array.wifi_eap_method_tts_strings);
1845 
1846         // Replace the target strings with tts strings and save all in a new array.
1847         final CharSequence[] newTtsSourceStrings = findAndReplaceTargetStrings(
1848                 sourceStrings, targetStrings, ttsStrings);
1849 
1850         // Build new TtsSpan text arrays for TalkBack.
1851         final CharSequence[] accessibilityArray = createAccessibleEntries(
1852                 sourceStrings, newTtsSourceStrings);
1853 
1854         // Return a new ArrayAdapter with the new TalkBack array.
1855         ArrayAdapter<CharSequence> spinnerAdapter = new ArrayAdapter<>(
1856                 mContext, android.R.layout.simple_spinner_item, accessibilityArray);
1857         spinnerAdapter.setDropDownViewResource(
1858                 android.R.layout.simple_spinner_dropdown_item);
1859         return spinnerAdapter;
1860     }
1861 
createAccessibleEntries(CharSequence entries[], CharSequence[] contentDescriptions)1862     private SpannableString[] createAccessibleEntries(CharSequence entries[],
1863             CharSequence[] contentDescriptions) {
1864         final SpannableString[] accessibleEntries = new SpannableString[entries.length];
1865         for (int i = 0; i < entries.length; i++) {
1866             accessibleEntries[i] = com.android.settings.Utils.createAccessibleSequence(entries[i],
1867                     contentDescriptions[i].toString());
1868         }
1869         return accessibleEntries;
1870     }
1871 
hideSoftKeyboard(IBinder windowToken)1872     private void hideSoftKeyboard(IBinder windowToken) {
1873         final InputMethodManager inputMethodManager = mContext.getSystemService(
1874                 InputMethodManager.class);
1875         inputMethodManager.hideSoftInputFromWindow(windowToken, 0 /* flags */);
1876     }
1877 
setAdvancedOptionAccessibilityString()1878     private void setAdvancedOptionAccessibilityString() {
1879         final CheckBox advancedToggleBox = mView.findViewById(R.id.wifi_advanced_togglebox);
1880         advancedToggleBox.setAccessibilityDelegate(new AccessibilityDelegate() {
1881             @Override
1882             public void onInitializeAccessibilityNodeInfo(
1883                     View v, AccessibilityNodeInfo info) {
1884                 super.onInitializeAccessibilityNodeInfo(v, info);
1885                 // To let TalkBack don't pronounce checked/unchecked.
1886                 info.setCheckable(false /* checkable */);
1887                 // To let TalkBack don't pronounce CheckBox.
1888                 info.setClassName(null /* className */);
1889                 // Customize TalkBack's pronunciation which been appended to "Double-tap to".
1890                 final AccessibilityAction customClick = new AccessibilityAction(
1891                         AccessibilityNodeInfo.ACTION_CLICK,
1892                         mContext.getString(R.string.wifi_advanced_toggle_description_collapsed));
1893                 info.addAction(customClick);
1894             }
1895         });
1896     }
1897 }
1898