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