• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2010 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.android.settings.wifi;
18 
19 import static android.net.wifi.WifiConfiguration.INVALID_NETWORK_ID;
20 
21 import android.app.ActionBar;
22 import android.app.Activity;
23 import android.app.AlertDialog;
24 import android.app.AlertDialog.Builder;
25 import android.app.Dialog;
26 import android.content.BroadcastReceiver;
27 import android.content.Context;
28 import android.content.DialogInterface;
29 import android.content.Intent;
30 import android.content.IntentFilter;
31 import android.content.pm.PackageManager;
32 import android.content.res.Resources;
33 import android.net.ConnectivityManager;
34 import android.net.NetworkInfo;
35 import android.net.NetworkInfo.DetailedState;
36 import android.net.wifi.ScanResult;
37 import android.net.wifi.SupplicantState;
38 import android.net.wifi.WifiConfiguration;
39 import android.net.wifi.WifiInfo;
40 import android.net.wifi.WifiManager;
41 import android.net.wifi.WpsInfo;
42 import android.os.Bundle;
43 import android.os.Handler;
44 import android.os.Message;
45 import android.preference.Preference;
46 import android.preference.PreferenceActivity;
47 import android.preference.PreferenceScreen;
48 import android.security.Credentials;
49 import android.security.KeyStore;
50 import android.telephony.TelephonyManager;
51 import android.util.AttributeSet;
52 import android.util.Log;
53 import android.view.ContextMenu;
54 import android.view.ContextMenu.ContextMenuInfo;
55 import android.view.Gravity;
56 import android.view.LayoutInflater;
57 import android.view.Menu;
58 import android.view.MenuInflater;
59 import android.view.MenuItem;
60 import android.view.View;
61 import android.view.View.OnClickListener;
62 import android.view.ViewGroup;
63 import android.widget.AdapterView.AdapterContextMenuInfo;
64 import android.widget.Button;
65 import android.widget.ImageButton;
66 import android.widget.PopupMenu;
67 import android.widget.PopupMenu.OnMenuItemClickListener;
68 import android.widget.RelativeLayout;
69 import android.widget.Switch;
70 import android.widget.TextView;
71 import android.widget.Toast;
72 
73 import com.android.settings.R;
74 import com.android.settings.SettingsPreferenceFragment;
75 import com.android.settings.wifi.p2p.WifiP2pSettings;
76 
77 import java.util.ArrayList;
78 import java.util.Collection;
79 import java.util.Collections;
80 import java.util.HashMap;
81 import java.util.List;
82 import java.util.concurrent.atomic.AtomicBoolean;
83 
84 /**
85  * Two types of UI are provided here.
86  *
87  * The first is for "usual Settings", appearing as any other Setup fragment.
88  *
89  * The second is for Setup Wizard, with a simplified interface that hides the action bar
90  * and menus.
91  */
92 public class WifiSettings extends SettingsPreferenceFragment
93         implements DialogInterface.OnClickListener  {
94     private static final String TAG = "WifiSettings";
95     private static final int MENU_ID_WPS_PBC = Menu.FIRST;
96     private static final int MENU_ID_WPS_PIN = Menu.FIRST + 1;
97     private static final int MENU_ID_P2P = Menu.FIRST + 2;
98     private static final int MENU_ID_ADD_NETWORK = Menu.FIRST + 3;
99     private static final int MENU_ID_ADVANCED = Menu.FIRST + 4;
100     private static final int MENU_ID_SCAN = Menu.FIRST + 5;
101     private static final int MENU_ID_CONNECT = Menu.FIRST + 6;
102     private static final int MENU_ID_FORGET = Menu.FIRST + 7;
103     private static final int MENU_ID_MODIFY = Menu.FIRST + 8;
104 
105     private static final int WIFI_DIALOG_ID = 1;
106     private static final int WPS_PBC_DIALOG_ID = 2;
107     private static final int WPS_PIN_DIALOG_ID = 3;
108     private static final int WIFI_SKIPPED_DIALOG_ID = 4;
109     private static final int WIFI_AND_MOBILE_SKIPPED_DIALOG_ID = 5;
110 
111     // Combo scans can take 5-6s to complete - set to 10s.
112     private static final int WIFI_RESCAN_INTERVAL_MS = 10 * 1000;
113 
114     // Instance state keys
115     private static final String SAVE_DIALOG_EDIT_MODE = "edit_mode";
116     private static final String SAVE_DIALOG_ACCESS_POINT_STATE = "wifi_ap_state";
117 
118     private final IntentFilter mFilter;
119     private final BroadcastReceiver mReceiver;
120     private final Scanner mScanner;
121 
122     private WifiManager mWifiManager;
123     private WifiManager.ActionListener mConnectListener;
124     private WifiManager.ActionListener mSaveListener;
125     private WifiManager.ActionListener mForgetListener;
126     private boolean mP2pSupported;
127 
128 
129     private WifiEnabler mWifiEnabler;
130     // An access point being editted is stored here.
131     private AccessPoint mSelectedAccessPoint;
132 
133     private DetailedState mLastState;
134     private WifiInfo mLastInfo;
135 
136     private AtomicBoolean mConnected = new AtomicBoolean(false);
137 
138     private int mKeyStoreNetworkId = INVALID_NETWORK_ID;
139 
140     private WifiDialog mDialog;
141 
142     private TextView mEmptyView;
143 
144     /* Used in Wifi Setup context */
145 
146     // this boolean extra specifies whether to disable the Next button when not connected
147     private static final String EXTRA_ENABLE_NEXT_ON_CONNECT = "wifi_enable_next_on_connect";
148 
149     // this boolean extra specifies whether to auto finish when connection is established
150     private static final String EXTRA_AUTO_FINISH_ON_CONNECT = "wifi_auto_finish_on_connect";
151 
152     // this boolean extra shows a custom button that we can control
153     protected static final String EXTRA_SHOW_CUSTOM_BUTTON = "wifi_show_custom_button";
154 
155     // show a text regarding data charges when wifi connection is required during setup wizard
156     protected static final String EXTRA_SHOW_WIFI_REQUIRED_INFO = "wifi_show_wifi_required_info";
157 
158     // this boolean extra is set if we are being invoked by the Setup Wizard
159     private static final String EXTRA_IS_FIRST_RUN = "firstRun";
160 
161     // should Next button only be enabled when we have a connection?
162     private boolean mEnableNextOnConnection;
163 
164     // should activity finish once we have a connection?
165     private boolean mAutoFinishOnConnection;
166 
167     // Save the dialog details
168     private boolean mDlgEdit;
169     private AccessPoint mDlgAccessPoint;
170     private Bundle mAccessPointSavedState;
171 
172     // the action bar uses a different set of controls for Setup Wizard
173     private boolean mSetupWizardMode;
174 
175     /* End of "used in Wifi Setup context" */
176 
WifiSettings()177     public WifiSettings() {
178         mFilter = new IntentFilter();
179         mFilter.addAction(WifiManager.WIFI_STATE_CHANGED_ACTION);
180         mFilter.addAction(WifiManager.SCAN_RESULTS_AVAILABLE_ACTION);
181         mFilter.addAction(WifiManager.NETWORK_IDS_CHANGED_ACTION);
182         mFilter.addAction(WifiManager.SUPPLICANT_STATE_CHANGED_ACTION);
183         mFilter.addAction(WifiManager.CONFIGURED_NETWORKS_CHANGED_ACTION);
184         mFilter.addAction(WifiManager.LINK_CONFIGURATION_CHANGED_ACTION);
185         mFilter.addAction(WifiManager.NETWORK_STATE_CHANGED_ACTION);
186         mFilter.addAction(WifiManager.RSSI_CHANGED_ACTION);
187 
188         mReceiver = new BroadcastReceiver() {
189             @Override
190             public void onReceive(Context context, Intent intent) {
191                 handleEvent(context, intent);
192             }
193         };
194 
195         mScanner = new Scanner();
196     }
197 
198     @Override
onCreate(Bundle icicle)199     public void onCreate(Bundle icicle) {
200         // Set this flag early, as it's needed by getHelpResource(), which is called by super
201         mSetupWizardMode = getActivity().getIntent().getBooleanExtra(EXTRA_IS_FIRST_RUN, false);
202 
203         super.onCreate(icicle);
204     }
205 
206     @Override
onCreateView(final LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState)207     public View onCreateView(final LayoutInflater inflater, ViewGroup container,
208             Bundle savedInstanceState) {
209         if (mSetupWizardMode) {
210             View view = inflater.inflate(R.layout.setup_preference, container, false);
211             View other = view.findViewById(R.id.other_network);
212             other.setOnClickListener(new OnClickListener() {
213                 @Override
214                 public void onClick(View v) {
215                     if (mWifiManager.isWifiEnabled()) {
216                         onAddNetworkPressed();
217                     }
218                 }
219             });
220             final ImageButton b = (ImageButton) view.findViewById(R.id.more);
221             if (b != null) {
222                 b.setOnClickListener(new OnClickListener() {
223                     @Override
224                     public void onClick(View v) {
225                         if (mWifiManager.isWifiEnabled()) {
226                             PopupMenu pm = new PopupMenu(inflater.getContext(), b);
227                             pm.inflate(R.menu.wifi_setup);
228                             pm.setOnMenuItemClickListener(new OnMenuItemClickListener() {
229                                 @Override
230                                 public boolean onMenuItemClick(MenuItem item) {
231                                     if (R.id.wifi_wps == item.getItemId()) {
232                                         showDialog(WPS_PBC_DIALOG_ID);
233                                         return true;
234                                     }
235                                     return false;
236                                 }
237                             });
238                             pm.show();
239                         }
240                     }
241                 });
242             }
243 
244             Intent intent = getActivity().getIntent();
245             if (intent.getBooleanExtra(EXTRA_SHOW_CUSTOM_BUTTON, false)) {
246                 view.findViewById(R.id.button_bar).setVisibility(View.VISIBLE);
247                 view.findViewById(R.id.back_button).setVisibility(View.INVISIBLE);
248                 view.findViewById(R.id.skip_button).setVisibility(View.INVISIBLE);
249                 view.findViewById(R.id.next_button).setVisibility(View.INVISIBLE);
250 
251                 Button customButton = (Button) view.findViewById(R.id.custom_button);
252                 customButton.setVisibility(View.VISIBLE);
253                 customButton.setOnClickListener(new OnClickListener() {
254                     @Override
255                     public void onClick(View v) {
256                         if (isPhone() && !hasSimProblem()) {
257                             showDialog(WIFI_SKIPPED_DIALOG_ID);
258                         } else {
259                             showDialog(WIFI_AND_MOBILE_SKIPPED_DIALOG_ID);
260                         }
261                     }
262                 });
263             }
264 
265             if (intent.getBooleanExtra(EXTRA_SHOW_WIFI_REQUIRED_INFO, false)) {
266                 view.findViewById(R.id.wifi_required_info).setVisibility(View.VISIBLE);
267             }
268 
269             return view;
270         } else {
271             return super.onCreateView(inflater, container, savedInstanceState);
272         }
273     }
274 
275     @Override
onActivityCreated(Bundle savedInstanceState)276     public void onActivityCreated(Bundle savedInstanceState) {
277         super.onActivityCreated(savedInstanceState);
278 
279         mP2pSupported = getPackageManager().hasSystemFeature(PackageManager.FEATURE_WIFI_DIRECT);
280         mWifiManager = (WifiManager) getSystemService(Context.WIFI_SERVICE);
281 
282         mConnectListener = new WifiManager.ActionListener() {
283                                    public void onSuccess() {
284                                    }
285                                    public void onFailure(int reason) {
286                                        Activity activity = getActivity();
287                                        if (activity != null) {
288                                            Toast.makeText(activity,
289                                                 R.string.wifi_failed_connect_message,
290                                                 Toast.LENGTH_SHORT).show();
291                                        }
292                                    }
293                                };
294 
295         mSaveListener = new WifiManager.ActionListener() {
296                                 public void onSuccess() {
297                                 }
298                                 public void onFailure(int reason) {
299                                     Activity activity = getActivity();
300                                     if (activity != null) {
301                                         Toast.makeText(activity,
302                                             R.string.wifi_failed_save_message,
303                                             Toast.LENGTH_SHORT).show();
304                                     }
305                                 }
306                             };
307 
308         mForgetListener = new WifiManager.ActionListener() {
309                                    public void onSuccess() {
310                                    }
311                                    public void onFailure(int reason) {
312                                        Activity activity = getActivity();
313                                        if (activity != null) {
314                                            Toast.makeText(activity,
315                                                R.string.wifi_failed_forget_message,
316                                                Toast.LENGTH_SHORT).show();
317                                        }
318                                    }
319                                };
320 
321         if (savedInstanceState != null
322                 && savedInstanceState.containsKey(SAVE_DIALOG_ACCESS_POINT_STATE)) {
323             mDlgEdit = savedInstanceState.getBoolean(SAVE_DIALOG_EDIT_MODE);
324             mAccessPointSavedState = savedInstanceState.getBundle(SAVE_DIALOG_ACCESS_POINT_STATE);
325         }
326 
327         final Activity activity = getActivity();
328         final Intent intent = activity.getIntent();
329 
330         // first if we're supposed to finish once we have a connection
331         mAutoFinishOnConnection = intent.getBooleanExtra(EXTRA_AUTO_FINISH_ON_CONNECT, false);
332 
333         if (mAutoFinishOnConnection) {
334             // Hide the next button
335             if (hasNextButton()) {
336                 getNextButton().setVisibility(View.GONE);
337             }
338 
339             final ConnectivityManager connectivity = (ConnectivityManager)
340                     activity.getSystemService(Context.CONNECTIVITY_SERVICE);
341             if (connectivity != null
342                     && connectivity.getNetworkInfo(ConnectivityManager.TYPE_WIFI).isConnected()) {
343                 activity.setResult(Activity.RESULT_OK);
344                 activity.finish();
345                 return;
346             }
347         }
348 
349         // if we're supposed to enable/disable the Next button based on our current connection
350         // state, start it off in the right state
351         mEnableNextOnConnection = intent.getBooleanExtra(EXTRA_ENABLE_NEXT_ON_CONNECT, false);
352 
353         if (mEnableNextOnConnection) {
354             if (hasNextButton()) {
355                 final ConnectivityManager connectivity = (ConnectivityManager)
356                         activity.getSystemService(Context.CONNECTIVITY_SERVICE);
357                 if (connectivity != null) {
358                     NetworkInfo info = connectivity.getNetworkInfo(
359                             ConnectivityManager.TYPE_WIFI);
360                     changeNextButtonState(info.isConnected());
361                 }
362             }
363         }
364 
365         addPreferencesFromResource(R.xml.wifi_settings);
366 
367         if (mSetupWizardMode) {
368             getView().setSystemUiVisibility(
369                     View.STATUS_BAR_DISABLE_BACK |
370                     View.STATUS_BAR_DISABLE_HOME |
371                     View.STATUS_BAR_DISABLE_RECENT |
372                     View.STATUS_BAR_DISABLE_NOTIFICATION_ALERTS |
373                     View.STATUS_BAR_DISABLE_CLOCK);
374         }
375 
376         // On/off switch is hidden for Setup Wizard
377         if (!mSetupWizardMode) {
378             Switch actionBarSwitch = new Switch(activity);
379 
380             if (activity instanceof PreferenceActivity) {
381                 PreferenceActivity preferenceActivity = (PreferenceActivity) activity;
382                 if (preferenceActivity.onIsHidingHeaders() || !preferenceActivity.onIsMultiPane()) {
383                     final int padding = activity.getResources().getDimensionPixelSize(
384                             R.dimen.action_bar_switch_padding);
385                     actionBarSwitch.setPadding(0, 0, padding, 0);
386                     activity.getActionBar().setDisplayOptions(ActionBar.DISPLAY_SHOW_CUSTOM,
387                             ActionBar.DISPLAY_SHOW_CUSTOM);
388                     activity.getActionBar().setCustomView(actionBarSwitch, new ActionBar.LayoutParams(
389                             ActionBar.LayoutParams.WRAP_CONTENT,
390                             ActionBar.LayoutParams.WRAP_CONTENT,
391                             Gravity.CENTER_VERTICAL | Gravity.END));
392                 }
393             }
394 
395             mWifiEnabler = new WifiEnabler(activity, actionBarSwitch);
396         }
397 
398         mEmptyView = (TextView) getView().findViewById(android.R.id.empty);
399         getListView().setEmptyView(mEmptyView);
400 
401         if (!mSetupWizardMode) {
402             registerForContextMenu(getListView());
403         }
404         setHasOptionsMenu(true);
405     }
406 
407     @Override
onResume()408     public void onResume() {
409         super.onResume();
410         if (mWifiEnabler != null) {
411             mWifiEnabler.resume();
412         }
413 
414         getActivity().registerReceiver(mReceiver, mFilter);
415         if (mKeyStoreNetworkId != INVALID_NETWORK_ID &&
416                 KeyStore.getInstance().state() == KeyStore.State.UNLOCKED) {
417             mWifiManager.connect(mKeyStoreNetworkId, mConnectListener);
418         }
419         mKeyStoreNetworkId = INVALID_NETWORK_ID;
420 
421         updateAccessPoints();
422     }
423 
424     @Override
onPause()425     public void onPause() {
426         super.onPause();
427         if (mWifiEnabler != null) {
428             mWifiEnabler.pause();
429         }
430         getActivity().unregisterReceiver(mReceiver);
431         mScanner.pause();
432     }
433 
434     @Override
onCreateOptionsMenu(Menu menu, MenuInflater inflater)435     public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
436         final boolean wifiIsEnabled = mWifiManager.isWifiEnabled();
437         if (mSetupWizardMode) {
438             // FIXME: add setIcon() when graphics are available
439             menu.add(Menu.NONE, MENU_ID_WPS_PBC, 0, R.string.wifi_menu_wps_pbc)
440                     .setIcon(R.drawable.ic_wps)
441                     .setEnabled(wifiIsEnabled)
442                     .setShowAsAction(MenuItem.SHOW_AS_ACTION_ALWAYS);
443             menu.add(Menu.NONE, MENU_ID_ADD_NETWORK, 0, R.string.wifi_add_network)
444                     .setEnabled(wifiIsEnabled)
445                     .setShowAsAction(MenuItem.SHOW_AS_ACTION_ALWAYS);
446         } else {
447             menu.add(Menu.NONE, MENU_ID_WPS_PBC, 0, R.string.wifi_menu_wps_pbc)
448                     .setIcon(R.drawable.ic_wps)
449                     .setEnabled(wifiIsEnabled)
450                     .setShowAsAction(MenuItem.SHOW_AS_ACTION_IF_ROOM);
451             menu.add(Menu.NONE, MENU_ID_ADD_NETWORK, 0, R.string.wifi_add_network)
452                     .setIcon(R.drawable.ic_menu_add)
453                     .setEnabled(wifiIsEnabled)
454                     .setShowAsAction(MenuItem.SHOW_AS_ACTION_IF_ROOM);
455             menu.add(Menu.NONE, MENU_ID_SCAN, 0, R.string.wifi_menu_scan)
456                     //.setIcon(R.drawable.ic_menu_scan_network)
457                     .setEnabled(wifiIsEnabled)
458                     .setShowAsAction(MenuItem.SHOW_AS_ACTION_NEVER);
459             menu.add(Menu.NONE, MENU_ID_WPS_PIN, 0, R.string.wifi_menu_wps_pin)
460                     .setEnabled(wifiIsEnabled)
461                     .setShowAsAction(MenuItem.SHOW_AS_ACTION_NEVER);
462             if (mP2pSupported) {
463                 menu.add(Menu.NONE, MENU_ID_P2P, 0, R.string.wifi_menu_p2p)
464                         .setEnabled(wifiIsEnabled)
465                         .setShowAsAction(MenuItem.SHOW_AS_ACTION_NEVER);
466             }
467             menu.add(Menu.NONE, MENU_ID_ADVANCED, 0, R.string.wifi_menu_advanced)
468                     //.setIcon(android.R.drawable.ic_menu_manage)
469                     .setShowAsAction(MenuItem.SHOW_AS_ACTION_NEVER);
470         }
471         super.onCreateOptionsMenu(menu, inflater);
472     }
473 
474     @Override
onSaveInstanceState(Bundle outState)475     public void onSaveInstanceState(Bundle outState) {
476         super.onSaveInstanceState(outState);
477 
478         // If the dialog is showing, save its state.
479         if (mDialog != null && mDialog.isShowing()) {
480             outState.putBoolean(SAVE_DIALOG_EDIT_MODE, mDlgEdit);
481             if (mDlgAccessPoint != null) {
482                 mAccessPointSavedState = new Bundle();
483                 mDlgAccessPoint.saveWifiState(mAccessPointSavedState);
484                 outState.putBundle(SAVE_DIALOG_ACCESS_POINT_STATE, mAccessPointSavedState);
485             }
486         }
487     }
488 
489     @Override
onOptionsItemSelected(MenuItem item)490     public boolean onOptionsItemSelected(MenuItem item) {
491         switch (item.getItemId()) {
492             case MENU_ID_WPS_PBC:
493                 showDialog(WPS_PBC_DIALOG_ID);
494                 return true;
495             case MENU_ID_P2P:
496                 if (getActivity() instanceof PreferenceActivity) {
497                     ((PreferenceActivity) getActivity()).startPreferencePanel(
498                             WifiP2pSettings.class.getCanonicalName(),
499                             null,
500                             R.string.wifi_p2p_settings_title, null,
501                             this, 0);
502                 } else {
503                     startFragment(this, WifiP2pSettings.class.getCanonicalName(), -1, null);
504                 }
505                 return true;
506             case MENU_ID_WPS_PIN:
507                 showDialog(WPS_PIN_DIALOG_ID);
508                 return true;
509             case MENU_ID_SCAN:
510                 if (mWifiManager.isWifiEnabled()) {
511                     mScanner.forceScan();
512                 }
513                 return true;
514             case MENU_ID_ADD_NETWORK:
515                 if (mWifiManager.isWifiEnabled()) {
516                     onAddNetworkPressed();
517                 }
518                 return true;
519             case MENU_ID_ADVANCED:
520                 if (getActivity() instanceof PreferenceActivity) {
521                     ((PreferenceActivity) getActivity()).startPreferencePanel(
522                             AdvancedWifiSettings.class.getCanonicalName(),
523                             null,
524                             R.string.wifi_advanced_titlebar, null,
525                             this, 0);
526                 } else {
527                     startFragment(this, AdvancedWifiSettings.class.getCanonicalName(), -1, null);
528                 }
529                 return true;
530         }
531         return super.onOptionsItemSelected(item);
532     }
533 
534     @Override
onCreateContextMenu(ContextMenu menu, View view, ContextMenuInfo info)535     public void onCreateContextMenu(ContextMenu menu, View view, ContextMenuInfo info) {
536         if (info instanceof AdapterContextMenuInfo) {
537             Preference preference = (Preference) getListView().getItemAtPosition(
538                     ((AdapterContextMenuInfo) info).position);
539 
540             if (preference instanceof AccessPoint) {
541                 mSelectedAccessPoint = (AccessPoint) preference;
542                 menu.setHeaderTitle(mSelectedAccessPoint.ssid);
543                 if (mSelectedAccessPoint.getLevel() != -1
544                         && mSelectedAccessPoint.getState() == null) {
545                     menu.add(Menu.NONE, MENU_ID_CONNECT, 0, R.string.wifi_menu_connect);
546                 }
547                 if (mSelectedAccessPoint.networkId != INVALID_NETWORK_ID) {
548                     menu.add(Menu.NONE, MENU_ID_FORGET, 0, R.string.wifi_menu_forget);
549                     menu.add(Menu.NONE, MENU_ID_MODIFY, 0, R.string.wifi_menu_modify);
550                 }
551             }
552         }
553     }
554 
555     @Override
onContextItemSelected(MenuItem item)556     public boolean onContextItemSelected(MenuItem item) {
557         if (mSelectedAccessPoint == null) {
558             return super.onContextItemSelected(item);
559         }
560         switch (item.getItemId()) {
561             case MENU_ID_CONNECT: {
562                 if (mSelectedAccessPoint.networkId != INVALID_NETWORK_ID) {
563                     if (!requireKeyStore(mSelectedAccessPoint.getConfig())) {
564                         mWifiManager.connect(mSelectedAccessPoint.networkId,
565                                 mConnectListener);
566                     }
567                 } else if (mSelectedAccessPoint.security == AccessPoint.SECURITY_NONE) {
568                     /** Bypass dialog for unsecured networks */
569                     mSelectedAccessPoint.generateOpenNetworkConfig();
570                     mWifiManager.connect(mSelectedAccessPoint.getConfig(),
571                             mConnectListener);
572                 } else {
573                     showDialog(mSelectedAccessPoint, true);
574                 }
575                 return true;
576             }
577             case MENU_ID_FORGET: {
578                 mWifiManager.forget(mSelectedAccessPoint.networkId, mForgetListener);
579                 return true;
580             }
581             case MENU_ID_MODIFY: {
582                 showDialog(mSelectedAccessPoint, true);
583                 return true;
584             }
585         }
586         return super.onContextItemSelected(item);
587     }
588 
589     @Override
onPreferenceTreeClick(PreferenceScreen screen, Preference preference)590     public boolean onPreferenceTreeClick(PreferenceScreen screen, Preference preference) {
591         if (preference instanceof AccessPoint) {
592             mSelectedAccessPoint = (AccessPoint) preference;
593             /** Bypass dialog for unsecured, unsaved networks */
594             if (mSelectedAccessPoint.security == AccessPoint.SECURITY_NONE &&
595                     mSelectedAccessPoint.networkId == INVALID_NETWORK_ID) {
596                 mSelectedAccessPoint.generateOpenNetworkConfig();
597                 mWifiManager.connect(mSelectedAccessPoint.getConfig(), mConnectListener);
598             } else {
599                 showDialog(mSelectedAccessPoint, false);
600             }
601         } else {
602             return super.onPreferenceTreeClick(screen, preference);
603         }
604         return true;
605     }
606 
showDialog(AccessPoint accessPoint, boolean edit)607     private void showDialog(AccessPoint accessPoint, boolean edit) {
608         if (mDialog != null) {
609             removeDialog(WIFI_DIALOG_ID);
610             mDialog = null;
611         }
612 
613         // Save the access point and edit mode
614         mDlgAccessPoint = accessPoint;
615         mDlgEdit = edit;
616 
617         showDialog(WIFI_DIALOG_ID);
618     }
619 
620     @Override
onCreateDialog(int dialogId)621     public Dialog onCreateDialog(int dialogId) {
622         switch (dialogId) {
623             case WIFI_DIALOG_ID:
624                 AccessPoint ap = mDlgAccessPoint; // For manual launch
625                 if (ap == null) { // For re-launch from saved state
626                     if (mAccessPointSavedState != null) {
627                         ap = new AccessPoint(getActivity(), mAccessPointSavedState);
628                         // For repeated orientation changes
629                         mDlgAccessPoint = ap;
630                     }
631                 }
632                 // If it's still null, fine, it's for Add Network
633                 mSelectedAccessPoint = ap;
634                 mDialog = new WifiDialog(getActivity(), this, ap, mDlgEdit);
635                 return mDialog;
636             case WPS_PBC_DIALOG_ID:
637                 return new WpsDialog(getActivity(), WpsInfo.PBC);
638             case WPS_PIN_DIALOG_ID:
639                 return new WpsDialog(getActivity(), WpsInfo.DISPLAY);
640             case WIFI_SKIPPED_DIALOG_ID:
641                 return new AlertDialog.Builder(getActivity())
642                             .setMessage(R.string.wifi_skipped_message)
643                             .setCancelable(false)
644                             .setNegativeButton(R.string.wifi_skip_anyway,
645                                     new DialogInterface.OnClickListener() {
646                                 @Override
647                                 public void onClick(DialogInterface dialog, int id) {
648                                     getActivity().setResult(Activity.RESULT_CANCELED);
649                                     getActivity().finish();
650                                 }
651                             })
652                             .setPositiveButton(R.string.wifi_dont_skip,
653                                     new DialogInterface.OnClickListener() {
654                                 @Override
655                                 public void onClick(DialogInterface dialog, int id) {
656                                 }
657                             })
658                             .create();
659             case WIFI_AND_MOBILE_SKIPPED_DIALOG_ID:
660                 return new AlertDialog.Builder(getActivity())
661                             .setMessage(R.string.wifi_and_mobile_skipped_message)
662                             .setCancelable(false)
663                             .setNegativeButton(R.string.wifi_skip_anyway,
664                                     new DialogInterface.OnClickListener() {
665                                 @Override
666                                 public void onClick(DialogInterface dialog, int id) {
667                                     getActivity().setResult(Activity.RESULT_CANCELED);
668                                     getActivity().finish();
669                                 }
670                             })
671                             .setPositiveButton(R.string.wifi_dont_skip,
672                                     new DialogInterface.OnClickListener() {
673                                 @Override
674                                 public void onClick(DialogInterface dialog, int id) {
675                                 }
676                             })
677                             .create();
678 
679         }
680         return super.onCreateDialog(dialogId);
681     }
682 
683     private boolean isPhone() {
684         final TelephonyManager telephonyManager = (TelephonyManager)this.getSystemService(
685                 Context.TELEPHONY_SERVICE);
686         return telephonyManager != null
687                 && telephonyManager.getPhoneType() != TelephonyManager.PHONE_TYPE_NONE;
688     }
689 
690     /**
691     * Return true if there's any SIM related impediment to connectivity.
692     * Treats Unknown as OK. (Only returns true if we're sure of a SIM problem.)
693     */
694    protected boolean hasSimProblem() {
695        final TelephonyManager telephonyManager = (TelephonyManager)this.getSystemService(
696                Context.TELEPHONY_SERVICE);
697        return telephonyManager != null
698                && telephonyManager.getCurrentPhoneType() == TelephonyManager.PHONE_TYPE_GSM
699                && telephonyManager.getSimState() != TelephonyManager.SIM_STATE_READY
700                && telephonyManager.getSimState() != TelephonyManager.SIM_STATE_UNKNOWN;
701    }
702 
703     private boolean requireKeyStore(WifiConfiguration config) {
704         if (WifiConfigController.requireKeyStore(config) &&
705                 KeyStore.getInstance().state() != KeyStore.State.UNLOCKED) {
706             mKeyStoreNetworkId = config.networkId;
707             Credentials.getInstance().unlock(getActivity());
708             return true;
709         }
710         return false;
711     }
712 
713     /**
714      * Shows the latest access points available with supplimental information like
715      * the strength of network and the security for it.
716      */
717     private void updateAccessPoints() {
718         // Safeguard from some delayed event handling
719         if (getActivity() == null) return;
720 
721         final int wifiState = mWifiManager.getWifiState();
722 
723         switch (wifiState) {
724             case WifiManager.WIFI_STATE_ENABLED:
725                 // AccessPoints are automatically sorted with TreeSet.
726                 final Collection<AccessPoint> accessPoints = constructAccessPoints();
727                 getPreferenceScreen().removeAll();
728                 if(accessPoints.size() == 0) {
729                     addMessagePreference(R.string.wifi_empty_list_wifi_on);
730                 }
731                 for (AccessPoint accessPoint : accessPoints) {
732                     getPreferenceScreen().addPreference(accessPoint);
733                 }
734                 break;
735 
736             case WifiManager.WIFI_STATE_ENABLING:
737                 getPreferenceScreen().removeAll();
738                 break;
739 
740             case WifiManager.WIFI_STATE_DISABLING:
741                 addMessagePreference(R.string.wifi_stopping);
742                 break;
743 
744             case WifiManager.WIFI_STATE_DISABLED:
745                 addMessagePreference(R.string.wifi_empty_list_wifi_off);
746                 break;
747         }
748     }
749 
750     private void addMessagePreference(int messageId) {
751         if (mEmptyView != null) mEmptyView.setText(messageId);
752         getPreferenceScreen().removeAll();
753     }
754 
755     /** Returns sorted list of access points */
756     private List<AccessPoint> constructAccessPoints() {
757         ArrayList<AccessPoint> accessPoints = new ArrayList<AccessPoint>();
758         /** Lookup table to more quickly update AccessPoints by only considering objects with the
759          * correct SSID.  Maps SSID -> List of AccessPoints with the given SSID.  */
760         Multimap<String, AccessPoint> apMap = new Multimap<String, AccessPoint>();
761 
762         final List<WifiConfiguration> configs = mWifiManager.getConfiguredNetworks();
763         if (configs != null) {
764             for (WifiConfiguration config : configs) {
765                 AccessPoint accessPoint = new AccessPoint(getActivity(), config);
766                 accessPoint.update(mLastInfo, mLastState);
767                 accessPoints.add(accessPoint);
768                 apMap.put(accessPoint.ssid, accessPoint);
769             }
770         }
771 
772         final List<ScanResult> results = mWifiManager.getScanResults();
773         if (results != null) {
774             for (ScanResult result : results) {
775                 // Ignore hidden and ad-hoc networks.
776                 if (result.SSID == null || result.SSID.length() == 0 ||
777                         result.capabilities.contains("[IBSS]")) {
778                     continue;
779                 }
780 
781                 boolean found = false;
782                 for (AccessPoint accessPoint : apMap.getAll(result.SSID)) {
783                     if (accessPoint.update(result))
784                         found = true;
785                 }
786                 if (!found) {
787                     AccessPoint accessPoint = new AccessPoint(getActivity(), result);
788                     accessPoints.add(accessPoint);
789                     apMap.put(accessPoint.ssid, accessPoint);
790                 }
791             }
792         }
793 
794         // Pre-sort accessPoints to speed preference insertion
795         Collections.sort(accessPoints);
796         return accessPoints;
797     }
798 
799     /** A restricted multimap for use in constructAccessPoints */
800     private class Multimap<K,V> {
801         private HashMap<K,List<V>> store = new HashMap<K,List<V>>();
802         /** retrieve a non-null list of values with key K */
803         List<V> getAll(K key) {
804             List<V> values = store.get(key);
805             return values != null ? values : Collections.<V>emptyList();
806         }
807 
808         void put(K key, V val) {
809             List<V> curVals = store.get(key);
810             if (curVals == null) {
811                 curVals = new ArrayList<V>(3);
812                 store.put(key, curVals);
813             }
814             curVals.add(val);
815         }
816     }
817 
818     private void handleEvent(Context context, Intent intent) {
819         String action = intent.getAction();
820         if (WifiManager.WIFI_STATE_CHANGED_ACTION.equals(action)) {
821             updateWifiState(intent.getIntExtra(WifiManager.EXTRA_WIFI_STATE,
822                     WifiManager.WIFI_STATE_UNKNOWN));
823         } else if (WifiManager.SCAN_RESULTS_AVAILABLE_ACTION.equals(action) ||
824                 WifiManager.CONFIGURED_NETWORKS_CHANGED_ACTION.equals(action) ||
825                 WifiManager.LINK_CONFIGURATION_CHANGED_ACTION.equals(action)) {
826                 updateAccessPoints();
827         } else if (WifiManager.SUPPLICANT_STATE_CHANGED_ACTION.equals(action)) {
828             //Ignore supplicant state changes when network is connected
829             //TODO: we should deprecate SUPPLICANT_STATE_CHANGED_ACTION and
830             //introduce a broadcast that combines the supplicant and network
831             //network state change events so the apps dont have to worry about
832             //ignoring supplicant state change when network is connected
833             //to get more fine grained information.
834             SupplicantState state = (SupplicantState) intent.getParcelableExtra(
835                     WifiManager.EXTRA_NEW_STATE);
836             if (!mConnected.get() && SupplicantState.isHandshakeState(state)) {
837                 updateConnectionState(WifiInfo.getDetailedStateOf(state));
838             }
839         } else if (WifiManager.NETWORK_STATE_CHANGED_ACTION.equals(action)) {
840             NetworkInfo info = (NetworkInfo) intent.getParcelableExtra(
841                     WifiManager.EXTRA_NETWORK_INFO);
842             mConnected.set(info.isConnected());
843             changeNextButtonState(info.isConnected());
844             updateAccessPoints();
845             updateConnectionState(info.getDetailedState());
846             if (mAutoFinishOnConnection && info.isConnected()) {
847                 Activity activity = getActivity();
848                 if (activity != null) {
849                     activity.setResult(Activity.RESULT_OK);
850                     activity.finish();
851                 }
852                 return;
853             }
854         } else if (WifiManager.RSSI_CHANGED_ACTION.equals(action)) {
855             updateConnectionState(null);
856         }
857     }
858 
859     private void updateConnectionState(DetailedState state) {
860         /* sticky broadcasts can call this when wifi is disabled */
861         if (!mWifiManager.isWifiEnabled()) {
862             mScanner.pause();
863             return;
864         }
865 
866         if (state == DetailedState.OBTAINING_IPADDR) {
867             mScanner.pause();
868         } else {
869             mScanner.resume();
870         }
871 
872         mLastInfo = mWifiManager.getConnectionInfo();
873         if (state != null) {
874             mLastState = state;
875         }
876 
877         for (int i = getPreferenceScreen().getPreferenceCount() - 1; i >= 0; --i) {
878             // Maybe there's a WifiConfigPreference
879             Preference preference = getPreferenceScreen().getPreference(i);
880             if (preference instanceof AccessPoint) {
881                 final AccessPoint accessPoint = (AccessPoint) preference;
882                 accessPoint.update(mLastInfo, mLastState);
883             }
884         }
885     }
886 
887     private void updateWifiState(int state) {
888         Activity activity = getActivity();
889         if (activity != null) {
890             activity.invalidateOptionsMenu();
891         }
892 
893         switch (state) {
894             case WifiManager.WIFI_STATE_ENABLED:
895                 mScanner.resume();
896                 return; // not break, to avoid the call to pause() below
897 
898             case WifiManager.WIFI_STATE_ENABLING:
899                 addMessagePreference(R.string.wifi_starting);
900                 break;
901 
902             case WifiManager.WIFI_STATE_DISABLED:
903                 addMessagePreference(R.string.wifi_empty_list_wifi_off);
904                 break;
905         }
906 
907         mLastInfo = null;
908         mLastState = null;
909         mScanner.pause();
910     }
911 
912     private class Scanner extends Handler {
913         private int mRetry = 0;
914 
915         void resume() {
916             if (!hasMessages(0)) {
917                 sendEmptyMessage(0);
918             }
919         }
920 
921         void forceScan() {
922             removeMessages(0);
923             sendEmptyMessage(0);
924         }
925 
926         void pause() {
927             mRetry = 0;
928             removeMessages(0);
929         }
930 
931         @Override
932         public void handleMessage(Message message) {
933             if (mWifiManager.startScanActive()) {
934                 mRetry = 0;
935             } else if (++mRetry >= 3) {
936                 mRetry = 0;
937                 Activity activity = getActivity();
938                 if (activity != null) {
939                     Toast.makeText(activity, R.string.wifi_fail_to_scan,
940                             Toast.LENGTH_LONG).show();
941                 }
942                 return;
943             }
944             sendEmptyMessageDelayed(0, WIFI_RESCAN_INTERVAL_MS);
945         }
946     }
947 
948     /**
949      * Renames/replaces "Next" button when appropriate. "Next" button usually exists in
950      * Wifi setup screens, not in usual wifi settings screen.
951      *
952      * @param connected true when the device is connected to a wifi network.
953      */
954     private void changeNextButtonState(boolean connected) {
955         if (mEnableNextOnConnection && hasNextButton()) {
956             getNextButton().setEnabled(connected);
957         }
958     }
959 
960     public void onClick(DialogInterface dialogInterface, int button) {
961         if (button == WifiDialog.BUTTON_FORGET && mSelectedAccessPoint != null) {
962             forget();
963         } else if (button == WifiDialog.BUTTON_SUBMIT) {
964             submit(mDialog.getController());
965         }
966     }
967 
968     /* package */ void submit(WifiConfigController configController) {
969 
970         final WifiConfiguration config = configController.getConfig();
971 
972         if (config == null) {
973             if (mSelectedAccessPoint != null
974                     && !requireKeyStore(mSelectedAccessPoint.getConfig())
975                     && mSelectedAccessPoint.networkId != INVALID_NETWORK_ID) {
976                 mWifiManager.connect(mSelectedAccessPoint.networkId,
977                         mConnectListener);
978             }
979         } else if (config.networkId != INVALID_NETWORK_ID) {
980             if (mSelectedAccessPoint != null) {
981                 mWifiManager.save(config, mSaveListener);
982             }
983         } else {
984             if (configController.isEdit() || requireKeyStore(config)) {
985                 mWifiManager.save(config, mSaveListener);
986             } else {
987                 mWifiManager.connect(config, mConnectListener);
988             }
989         }
990 
991         if (mWifiManager.isWifiEnabled()) {
992             mScanner.resume();
993         }
994         updateAccessPoints();
995     }
996 
997     /* package */ void forget() {
998         if (mSelectedAccessPoint.networkId == INVALID_NETWORK_ID) {
999             // Should not happen, but a monkey seems to triger it
1000             Log.e(TAG, "Failed to forget invalid network " + mSelectedAccessPoint.getConfig());
1001             return;
1002         }
1003 
1004         mWifiManager.forget(mSelectedAccessPoint.networkId, mForgetListener);
1005 
1006         if (mWifiManager.isWifiEnabled()) {
1007             mScanner.resume();
1008         }
1009         updateAccessPoints();
1010 
1011         // We need to rename/replace "Next" button in wifi setup context.
1012         changeNextButtonState(false);
1013     }
1014 
1015     /**
1016      * Refreshes acccess points and ask Wifi module to scan networks again.
1017      */
1018     /* package */ void refreshAccessPoints() {
1019         if (mWifiManager.isWifiEnabled()) {
1020             mScanner.resume();
1021         }
1022 
1023         getPreferenceScreen().removeAll();
1024     }
1025 
1026     /**
1027      * Called when "add network" button is pressed.
1028      */
1029     /* package */ void onAddNetworkPressed() {
1030         // No exact access point is selected.
1031         mSelectedAccessPoint = null;
1032         showDialog(null, true);
1033     }
1034 
1035     /* package */ int getAccessPointsCount() {
1036         final boolean wifiIsEnabled = mWifiManager.isWifiEnabled();
1037         if (wifiIsEnabled) {
1038             return getPreferenceScreen().getPreferenceCount();
1039         } else {
1040             return 0;
1041         }
1042     }
1043 
1044     /**
1045      * Requests wifi module to pause wifi scan. May be ignored when the module is disabled.
1046      */
1047     /* package */ void pauseWifiScan() {
1048         if (mWifiManager.isWifiEnabled()) {
1049             mScanner.pause();
1050         }
1051     }
1052 
1053     /**
1054      * Requests wifi module to resume wifi scan. May be ignored when the module is disabled.
1055      */
1056     /* package */ void resumeWifiScan() {
1057         if (mWifiManager.isWifiEnabled()) {
1058             mScanner.resume();
1059         }
1060     }
1061 
1062     @Override
1063     protected int getHelpResource() {
1064         if (mSetupWizardMode) {
1065             return 0;
1066         }
1067         return R.string.help_url_wifi;
1068     }
1069 
1070     /**
1071      * Used as the outer frame of all setup wizard pages that need to adjust their margins based
1072      * on the total size of the available display. (e.g. side margins set to 10% of total width.)
1073      */
1074     public static class ProportionalOuterFrame extends RelativeLayout {
1075         public ProportionalOuterFrame(Context context) {
1076             super(context);
1077         }
1078         public ProportionalOuterFrame(Context context, AttributeSet attrs) {
1079             super(context, attrs);
1080         }
1081         public ProportionalOuterFrame(Context context, AttributeSet attrs, int defStyle) {
1082             super(context, attrs, defStyle);
1083         }
1084 
1085         /**
1086          * Set our margins and title area height proportionally to the available display size
1087          */
1088         @Override
1089         protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
1090             int parentWidth = MeasureSpec.getSize(widthMeasureSpec);
1091             int parentHeight = MeasureSpec.getSize(heightMeasureSpec);
1092             final Resources resources = getContext().getResources();
1093             float titleHeight = resources.getFraction(R.dimen.setup_title_height, 1, 1);
1094             float sideMargin = resources.getFraction(R.dimen.setup_border_width, 1, 1);
1095             int bottom = resources.getDimensionPixelSize(R.dimen.setup_margin_bottom);
1096             setPadding(
1097                     (int) (parentWidth * sideMargin),
1098                     0,
1099                     (int) (parentWidth * sideMargin),
1100                     bottom);
1101             View title = findViewById(R.id.title_area);
1102             if (title != null) {
1103                 title.setMinimumHeight((int) (parentHeight * titleHeight));
1104             }
1105             super.onMeasure(widthMeasureSpec, heightMeasureSpec);
1106         }
1107     }
1108 
1109 }
1110