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