• 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 import static android.os.UserManager.DISALLOW_CONFIG_WIFI;
21 
22 import android.app.Activity;
23 import android.app.ActivityManager;
24 import android.app.Dialog;
25 import android.content.BroadcastReceiver;
26 import android.content.Context;
27 import android.content.DialogInterface;
28 import android.content.Intent;
29 import android.content.IntentFilter;
30 import android.content.SharedPreferences;
31 import android.content.res.Resources;
32 import android.content.res.TypedArray;
33 import android.location.LocationManager;
34 import android.net.ConnectivityManager;
35 import android.net.NetworkInfo;
36 import android.net.NetworkInfo.DetailedState;
37 import android.net.NetworkScoreManager;
38 import android.net.NetworkScorerAppManager;
39 import android.net.NetworkScorerAppManager.NetworkScorerAppData;
40 import android.net.wifi.ScanResult;
41 import android.net.wifi.WifiConfiguration;
42 import android.net.wifi.WifiInfo;
43 import android.net.wifi.WifiManager;
44 import android.net.wifi.WpsInfo;
45 import android.os.Build;
46 import android.os.Bundle;
47 import android.os.Handler;
48 import android.os.Message;
49 import android.os.UserHandle;
50 import android.preference.Preference;
51 import android.preference.PreferenceScreen;
52 import android.util.Log;
53 import android.view.ContextMenu;
54 import android.view.ContextMenu.ContextMenuInfo;
55 import android.view.LayoutInflater;
56 import android.view.Menu;
57 import android.view.MenuInflater;
58 import android.view.MenuItem;
59 import android.view.View;
60 import android.view.View.OnClickListener;
61 import android.widget.AdapterView.AdapterContextMenuInfo;
62 import android.widget.Button;
63 import android.widget.TextView;
64 import android.widget.Toast;
65 
66 import com.android.settings.R;
67 import com.android.settings.RestrictedSettingsFragment;
68 import com.android.settings.SettingsActivity;
69 import com.android.settings.search.BaseSearchIndexProvider;
70 import com.android.settings.search.Indexable;
71 import com.android.settings.search.SearchIndexableRaw;
72 
73 import java.util.ArrayList;
74 import java.util.Collection;
75 import java.util.Collections;
76 import java.util.HashMap;
77 import java.util.List;
78 import java.util.concurrent.atomic.AtomicBoolean;
79 
80 /**
81  * Two types of UI are provided here.
82  *
83  * The first is for "usual Settings", appearing as any other Setup fragment.
84  *
85  * The second is for Setup Wizard, with a simplified interface that hides the action bar
86  * and menus.
87  */
88 public class WifiSettings extends RestrictedSettingsFragment
89         implements DialogInterface.OnClickListener, Indexable  {
90 
91     private static final String TAG = "WifiSettings";
92 
93     private static final int REQUEST_ENABLE_WIFI_ASSISTANT = 1;
94 
95     /* package */ 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_SAVED_NETWORK = Menu.FIRST + 2;
98     /* package */ 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     private static final int MENU_ID_WRITE_NFC = Menu.FIRST + 9;
105 
106     private static final String KEY_ASSISTANT_DISMISS_PLATFORM = "assistant_dismiss_platform";
107 
108     public static final int WIFI_DIALOG_ID = 1;
109     /* package */ static final int WPS_PBC_DIALOG_ID = 2;
110     private static final int WPS_PIN_DIALOG_ID = 3;
111     private static final int WRITE_NFC_DIALOG_ID = 6;
112 
113     // Combo scans can take 5-6s to complete - set to 10s.
114     private static final int WIFI_RESCAN_INTERVAL_MS = 10 * 1000;
115 
116     // Instance state keys
117     private static final String SAVE_DIALOG_EDIT_MODE = "edit_mode";
118     private static final String SAVE_DIALOG_ACCESS_POINT_STATE = "wifi_ap_state";
119 
120     private static boolean savedNetworksExist;
121 
122     private final IntentFilter mFilter;
123     private final BroadcastReceiver mReceiver;
124     private final Scanner mScanner;
125 
126     /* package */ WifiManager mWifiManager;
127     private WifiManager.ActionListener mConnectListener;
128     private WifiManager.ActionListener mSaveListener;
129     private WifiManager.ActionListener mForgetListener;
130 
131     private WifiEnabler mWifiEnabler;
132     // An access point being editted is stored here.
133     private AccessPoint mSelectedAccessPoint;
134 
135     private DetailedState mLastState;
136     private WifiInfo mLastInfo;
137 
138     private final AtomicBoolean mConnected = new AtomicBoolean(false);
139 
140     private WifiDialog mDialog;
141     private WriteWifiConfigToNfcDialog mWifiToNfcDialog;
142 
143     private TextView mEmptyView;
144 
145     // this boolean extra specifies whether to disable the Next button when not connected. Used by
146     // account creation outside of setup wizard.
147     private static final String EXTRA_ENABLE_NEXT_ON_CONNECT = "wifi_enable_next_on_connect";
148 
149     // should Next button only be enabled when we have a connection?
150     private boolean mEnableNextOnConnection;
151 
152     // Save the dialog details
153     private boolean mDlgEdit;
154     private AccessPoint mDlgAccessPoint;
155     private Bundle mAccessPointSavedState;
156     private View mWifiAssistantCard;
157     private NetworkScorerAppData mWifiAssistantApp;
158 
159     /** verbose logging flag. this flag is set thru developer debugging options
160      * and used so as to assist with in-the-field WiFi connectivity debugging  */
161     public static int mVerboseLogging = 0;
162 
163     /* End of "used in Wifi Setup context" */
164 
165     /** A restricted multimap for use in constructAccessPoints */
166     private static class Multimap<K,V> {
167         private final HashMap<K,List<V>> store = new HashMap<K,List<V>>();
168         /** retrieve a non-null list of values with key K */
getAll(K key)169         List<V> getAll(K key) {
170             List<V> values = store.get(key);
171             return values != null ? values : Collections.<V>emptyList();
172         }
173 
put(K key, V val)174         void put(K key, V val) {
175             List<V> curVals = store.get(key);
176             if (curVals == null) {
177                 curVals = new ArrayList<V>(3);
178                 store.put(key, curVals);
179             }
180             curVals.add(val);
181         }
182     }
183 
184     private static class Scanner extends Handler {
185         private int mRetry = 0;
186         private WifiSettings mWifiSettings = null;
187 
Scanner(WifiSettings wifiSettings)188         Scanner(WifiSettings wifiSettings) {
189             mWifiSettings = wifiSettings;
190         }
191 
resume()192         void resume() {
193             if (!hasMessages(0)) {
194                 sendEmptyMessage(0);
195             }
196         }
197 
forceScan()198         void forceScan() {
199             removeMessages(0);
200             sendEmptyMessage(0);
201         }
202 
pause()203         void pause() {
204             mRetry = 0;
205             removeMessages(0);
206         }
207 
208         @Override
handleMessage(Message message)209         public void handleMessage(Message message) {
210             if (mWifiSettings.mWifiManager.startScan()) {
211                 mRetry = 0;
212             } else if (++mRetry >= 3) {
213                 mRetry = 0;
214                 Activity activity = mWifiSettings.getActivity();
215                 if (activity != null) {
216                     Toast.makeText(activity, R.string.wifi_fail_to_scan, Toast.LENGTH_LONG).show();
217                 }
218                 return;
219             }
220             sendEmptyMessageDelayed(0, WIFI_RESCAN_INTERVAL_MS);
221         }
222     }
223 
WifiSettings()224     public WifiSettings() {
225         super(DISALLOW_CONFIG_WIFI);
226         mFilter = new IntentFilter();
227         mFilter.addAction(WifiManager.WIFI_STATE_CHANGED_ACTION);
228         mFilter.addAction(WifiManager.SCAN_RESULTS_AVAILABLE_ACTION);
229         mFilter.addAction(WifiManager.NETWORK_IDS_CHANGED_ACTION);
230         mFilter.addAction(WifiManager.SUPPLICANT_STATE_CHANGED_ACTION);
231         mFilter.addAction(WifiManager.CONFIGURED_NETWORKS_CHANGED_ACTION);
232         mFilter.addAction(WifiManager.LINK_CONFIGURATION_CHANGED_ACTION);
233         mFilter.addAction(WifiManager.NETWORK_STATE_CHANGED_ACTION);
234         mFilter.addAction(WifiManager.RSSI_CHANGED_ACTION);
235 
236         mReceiver = new BroadcastReceiver() {
237             @Override
238             public void onReceive(Context context, Intent intent) {
239                 handleEvent(intent);
240             }
241         };
242 
243         mScanner = new Scanner(this);
244     }
245 
246     @Override
onActivityCreated(Bundle savedInstanceState)247     public void onActivityCreated(Bundle savedInstanceState) {
248         super.onActivityCreated(savedInstanceState);
249 
250         mWifiManager = (WifiManager) getSystemService(Context.WIFI_SERVICE);
251 
252         mConnectListener = new WifiManager.ActionListener() {
253                                    @Override
254                                    public void onSuccess() {
255                                    }
256                                    @Override
257                                    public void onFailure(int reason) {
258                                        Activity activity = getActivity();
259                                        if (activity != null) {
260                                            Toast.makeText(activity,
261                                                 R.string.wifi_failed_connect_message,
262                                                 Toast.LENGTH_SHORT).show();
263                                        }
264                                    }
265                                };
266 
267         mSaveListener = new WifiManager.ActionListener() {
268                                 @Override
269                                 public void onSuccess() {
270                                 }
271                                 @Override
272                                 public void onFailure(int reason) {
273                                     Activity activity = getActivity();
274                                     if (activity != null) {
275                                         Toast.makeText(activity,
276                                             R.string.wifi_failed_save_message,
277                                             Toast.LENGTH_SHORT).show();
278                                     }
279                                 }
280                             };
281 
282         mForgetListener = new WifiManager.ActionListener() {
283                                    @Override
284                                    public void onSuccess() {
285                                    }
286                                    @Override
287                                    public void onFailure(int reason) {
288                                        Activity activity = getActivity();
289                                        if (activity != null) {
290                                            Toast.makeText(activity,
291                                                R.string.wifi_failed_forget_message,
292                                                Toast.LENGTH_SHORT).show();
293                                        }
294                                    }
295                                };
296 
297         if (savedInstanceState != null) {
298             mDlgEdit = savedInstanceState.getBoolean(SAVE_DIALOG_EDIT_MODE);
299             if (savedInstanceState.containsKey(SAVE_DIALOG_ACCESS_POINT_STATE)) {
300                 mAccessPointSavedState =
301                     savedInstanceState.getBundle(SAVE_DIALOG_ACCESS_POINT_STATE);
302             }
303         }
304 
305         // if we're supposed to enable/disable the Next button based on our current connection
306         // state, start it off in the right state
307         Intent intent = getActivity().getIntent();
308         mEnableNextOnConnection = intent.getBooleanExtra(EXTRA_ENABLE_NEXT_ON_CONNECT, false);
309 
310         if (mEnableNextOnConnection) {
311             if (hasNextButton()) {
312                 final ConnectivityManager connectivity = (ConnectivityManager)
313                         getActivity().getSystemService(Context.CONNECTIVITY_SERVICE);
314                 if (connectivity != null) {
315                     NetworkInfo info = connectivity.getNetworkInfo(
316                             ConnectivityManager.TYPE_WIFI);
317                     changeNextButtonState(info.isConnected());
318                 }
319             }
320         }
321 
322         addPreferencesFromResource(R.xml.wifi_settings);
323 
324         prepareWifiAssistantCard();
325 
326         mEmptyView = initEmptyView();
327         registerForContextMenu(getListView());
328         setHasOptionsMenu(true);
329     }
330 
331     @Override
onActivityResult(int requestCode, int resultCode, Intent resultData)332     public void onActivityResult(int requestCode, int resultCode, Intent resultData) {
333         if (requestCode == REQUEST_ENABLE_WIFI_ASSISTANT) {
334             if (resultCode == Activity.RESULT_OK) {
335                 disableWifiAssistantCardUntilPlatformUpgrade();
336                 getListView().removeHeaderView(mWifiAssistantCard);
337                 mWifiAssistantApp = null;
338             }
339         } else {
340             super.onActivityResult(requestCode, resultCode, resultData);
341         }
342     }
343 
344     @Override
onDestroyView()345     public void onDestroyView() {
346         super.onDestroyView();
347 
348         if (mWifiEnabler != null) {
349             mWifiEnabler.teardownSwitchBar();
350         }
351     }
352 
353     @Override
onStart()354     public void onStart() {
355         super.onStart();
356 
357         // On/off switch is hidden for Setup Wizard (returns null)
358         mWifiEnabler = createWifiEnabler();
359     }
360 
361     /**
362      * @return new WifiEnabler or null (as overridden by WifiSettingsForSetupWizard)
363      */
createWifiEnabler()364     /* package */ WifiEnabler createWifiEnabler() {
365         final SettingsActivity activity = (SettingsActivity) getActivity();
366         return new WifiEnabler(activity, activity.getSwitchBar());
367     }
368 
369     @Override
onResume()370     public void onResume() {
371         final Activity activity = getActivity();
372         super.onResume();
373         if (mWifiEnabler != null) {
374             mWifiEnabler.resume(activity);
375         }
376 
377         activity.registerReceiver(mReceiver, mFilter);
378         updateAccessPoints();
379     }
380 
381     @Override
onPause()382     public void onPause() {
383         super.onPause();
384         if (mWifiEnabler != null) {
385             mWifiEnabler.pause();
386         }
387 
388         getActivity().unregisterReceiver(mReceiver);
389         mScanner.pause();
390     }
391 
392     @Override
onCreateOptionsMenu(Menu menu, MenuInflater inflater)393     public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
394         // If the user is not allowed to configure wifi, do not show the menu.
395         if (isUiRestricted()) return;
396 
397         addOptionsMenuItems(menu);
398         super.onCreateOptionsMenu(menu, inflater);
399     }
400 
401     /**
402      * @param menu
403      */
addOptionsMenuItems(Menu menu)404     void addOptionsMenuItems(Menu menu) {
405         final boolean wifiIsEnabled = mWifiManager.isWifiEnabled();
406         TypedArray ta = getActivity().getTheme().obtainStyledAttributes(
407                 new int[] {R.attr.ic_menu_add, R.attr.ic_wps});
408         menu.add(Menu.NONE, MENU_ID_ADD_NETWORK, 0, R.string.wifi_add_network)
409                 .setIcon(ta.getDrawable(0))
410                 .setEnabled(wifiIsEnabled)
411                 .setShowAsAction(MenuItem.SHOW_AS_ACTION_NEVER);
412         if (savedNetworksExist) {
413             menu.add(Menu.NONE, MENU_ID_SAVED_NETWORK, 0, R.string.wifi_saved_access_points_label)
414                     .setIcon(ta.getDrawable(0))
415                     .setEnabled(wifiIsEnabled)
416                     .setShowAsAction(MenuItem.SHOW_AS_ACTION_NEVER);
417         }
418         menu.add(Menu.NONE, MENU_ID_SCAN, 0, R.string.menu_stats_refresh)
419                .setEnabled(wifiIsEnabled)
420                .setShowAsAction(MenuItem.SHOW_AS_ACTION_NEVER);
421         menu.add(Menu.NONE, MENU_ID_ADVANCED, 0, R.string.wifi_menu_advanced)
422                 .setShowAsAction(MenuItem.SHOW_AS_ACTION_NEVER);
423         ta.recycle();
424     }
425 
426     @Override
onSaveInstanceState(Bundle outState)427     public void onSaveInstanceState(Bundle outState) {
428         super.onSaveInstanceState(outState);
429 
430         // If the dialog is showing, save its state.
431         if (mDialog != null && mDialog.isShowing()) {
432             outState.putBoolean(SAVE_DIALOG_EDIT_MODE, mDlgEdit);
433             if (mDlgAccessPoint != null) {
434                 mAccessPointSavedState = new Bundle();
435                 mDlgAccessPoint.saveWifiState(mAccessPointSavedState);
436                 outState.putBundle(SAVE_DIALOG_ACCESS_POINT_STATE, mAccessPointSavedState);
437             }
438         }
439     }
440 
441     @Override
onOptionsItemSelected(MenuItem item)442     public boolean onOptionsItemSelected(MenuItem item) {
443         // If the user is not allowed to configure wifi, do not handle menu selections.
444         if (isUiRestricted()) return false;
445 
446         switch (item.getItemId()) {
447             case MENU_ID_WPS_PBC:
448                 showDialog(WPS_PBC_DIALOG_ID);
449                 return true;
450                 /*
451             case MENU_ID_P2P:
452                 if (getActivity() instanceof SettingsActivity) {
453                     ((SettingsActivity) getActivity()).startPreferencePanel(
454                             WifiP2pSettings.class.getCanonicalName(),
455                             null,
456                             R.string.wifi_p2p_settings_title, null,
457                             this, 0);
458                 } else {
459                     startFragment(this, WifiP2pSettings.class.getCanonicalName(),
460                             R.string.wifi_p2p_settings_title, -1, null);
461                 }
462                 return true;
463                 */
464             case MENU_ID_WPS_PIN:
465                 showDialog(WPS_PIN_DIALOG_ID);
466                 return true;
467             case MENU_ID_SCAN:
468                 if (mWifiManager.isWifiEnabled()) {
469                     mScanner.forceScan();
470                 }
471                 return true;
472             case MENU_ID_ADD_NETWORK:
473                 if (mWifiManager.isWifiEnabled()) {
474                     onAddNetworkPressed();
475                 }
476                 return true;
477             case MENU_ID_SAVED_NETWORK:
478                 if (getActivity() instanceof SettingsActivity) {
479                     ((SettingsActivity) getActivity()).startPreferencePanel(
480                             SavedAccessPointsWifiSettings.class.getCanonicalName(), null,
481                             R.string.wifi_saved_access_points_titlebar, null, this, 0);
482                 } else {
483                     startFragment(this, SavedAccessPointsWifiSettings.class.getCanonicalName(),
484                             R.string.wifi_saved_access_points_titlebar,
485                             -1 /* Do not request a result */, null);
486                 }
487                 return true;
488             case MENU_ID_ADVANCED:
489                 if (getActivity() instanceof SettingsActivity) {
490                     ((SettingsActivity) getActivity()).startPreferencePanel(
491                             AdvancedWifiSettings.class.getCanonicalName(), null,
492                             R.string.wifi_advanced_titlebar, null, this, 0);
493                 } else {
494                     startFragment(this, AdvancedWifiSettings.class.getCanonicalName(),
495                             R.string.wifi_advanced_titlebar, -1 /* Do not request a results */,
496                             null);
497                 }
498                 return true;
499         }
500         return super.onOptionsItemSelected(item);
501     }
502 
503     @Override
onCreateContextMenu(ContextMenu menu, View view, ContextMenuInfo info)504     public void onCreateContextMenu(ContextMenu menu, View view, ContextMenuInfo info) {
505         if (info instanceof AdapterContextMenuInfo) {
506             Preference preference = (Preference) getListView().getItemAtPosition(
507                     ((AdapterContextMenuInfo) info).position);
508 
509             if (preference instanceof AccessPoint) {
510                 mSelectedAccessPoint = (AccessPoint) preference;
511                 menu.setHeaderTitle(mSelectedAccessPoint.ssid);
512                 if (mSelectedAccessPoint.getLevel() != -1
513                         && mSelectedAccessPoint.getState() == null) {
514                     menu.add(Menu.NONE, MENU_ID_CONNECT, 0, R.string.wifi_menu_connect);
515                 }
516                 if (mSelectedAccessPoint.networkId != INVALID_NETWORK_ID) {
517                     if (ActivityManager.getCurrentUser() == UserHandle.USER_OWNER) {
518                         menu.add(Menu.NONE, MENU_ID_FORGET, 0, R.string.wifi_menu_forget);
519                     }
520                     menu.add(Menu.NONE, MENU_ID_MODIFY, 0, R.string.wifi_menu_modify);
521 
522                     if (mSelectedAccessPoint.security != AccessPoint.SECURITY_NONE) {
523                         // Only allow writing of NFC tags for password-protected networks.
524                         menu.add(Menu.NONE, MENU_ID_WRITE_NFC, 0, R.string.wifi_menu_write_to_nfc);
525                     }
526                 }
527             }
528         }
529     }
530 
531     @Override
onContextItemSelected(MenuItem item)532     public boolean onContextItemSelected(MenuItem item) {
533         if (mSelectedAccessPoint == null) {
534             return super.onContextItemSelected(item);
535         }
536         switch (item.getItemId()) {
537             case MENU_ID_CONNECT: {
538                 if (mSelectedAccessPoint.networkId != INVALID_NETWORK_ID) {
539                     connect(mSelectedAccessPoint.networkId);
540                 } else if (mSelectedAccessPoint.security == AccessPoint.SECURITY_NONE) {
541                     /** Bypass dialog for unsecured networks */
542                     mSelectedAccessPoint.generateOpenNetworkConfig();
543                     connect(mSelectedAccessPoint.getConfig());
544                 } else {
545                     showDialog(mSelectedAccessPoint, true);
546                 }
547                 return true;
548             }
549             case MENU_ID_FORGET: {
550                 mWifiManager.forget(mSelectedAccessPoint.networkId, mForgetListener);
551                 return true;
552             }
553             case MENU_ID_MODIFY: {
554                 showDialog(mSelectedAccessPoint, true);
555                 return true;
556             }
557             case MENU_ID_WRITE_NFC:
558                 showDialog(WRITE_NFC_DIALOG_ID);
559                 return true;
560 
561         }
562         return super.onContextItemSelected(item);
563     }
564 
565     @Override
onPreferenceTreeClick(PreferenceScreen screen, Preference preference)566     public boolean onPreferenceTreeClick(PreferenceScreen screen, Preference preference) {
567         if (preference instanceof AccessPoint) {
568             mSelectedAccessPoint = (AccessPoint) preference;
569             /** Bypass dialog for unsecured, unsaved networks */
570             if (mSelectedAccessPoint.security == AccessPoint.SECURITY_NONE &&
571                     mSelectedAccessPoint.networkId == INVALID_NETWORK_ID) {
572                 mSelectedAccessPoint.generateOpenNetworkConfig();
573                 if (!savedNetworksExist) {
574                     savedNetworksExist = true;
575                     getActivity().invalidateOptionsMenu();
576                 }
577                 connect(mSelectedAccessPoint.getConfig());
578             } else {
579                 showDialog(mSelectedAccessPoint, false);
580             }
581         } else {
582             return super.onPreferenceTreeClick(screen, preference);
583         }
584         return true;
585     }
586 
showDialog(AccessPoint accessPoint, boolean edit)587     private void showDialog(AccessPoint accessPoint, boolean edit) {
588         if (mDialog != null) {
589             removeDialog(WIFI_DIALOG_ID);
590             mDialog = null;
591         }
592 
593         // Save the access point and edit mode
594         mDlgAccessPoint = accessPoint;
595         mDlgEdit = edit;
596 
597         showDialog(WIFI_DIALOG_ID);
598     }
599 
600     @Override
onCreateDialog(int dialogId)601     public Dialog onCreateDialog(int dialogId) {
602         switch (dialogId) {
603             case WIFI_DIALOG_ID:
604                 AccessPoint ap = mDlgAccessPoint; // For manual launch
605                 if (ap == null) { // For re-launch from saved state
606                     if (mAccessPointSavedState != null) {
607                         ap = new AccessPoint(getActivity(), mAccessPointSavedState);
608                         // For repeated orientation changes
609                         mDlgAccessPoint = ap;
610                         // Reset the saved access point data
611                         mAccessPointSavedState = null;
612                     }
613                 }
614                 // If it's null, fine, it's for Add Network
615                 mSelectedAccessPoint = ap;
616                 mDialog = new WifiDialog(getActivity(), this, ap, mDlgEdit);
617                 return mDialog;
618             case WPS_PBC_DIALOG_ID:
619                 return new WpsDialog(getActivity(), WpsInfo.PBC);
620             case WPS_PIN_DIALOG_ID:
621                 return new WpsDialog(getActivity(), WpsInfo.DISPLAY);
622             case WRITE_NFC_DIALOG_ID:
623                 if (mSelectedAccessPoint != null) {
624                     mWifiToNfcDialog = new WriteWifiConfigToNfcDialog(
625                             getActivity(), mSelectedAccessPoint, mWifiManager);
626                     return mWifiToNfcDialog;
627                 }
628 
629         }
630         return super.onCreateDialog(dialogId);
631     }
632 
633     /**
634      * Shows the latest access points available with supplemental information like
635      * the strength of network and the security for it.
636      */
updateAccessPoints()637     private void updateAccessPoints() {
638         // Safeguard from some delayed event handling
639         if (getActivity() == null) return;
640 
641         if (isUiRestricted()) {
642             addMessagePreference(R.string.wifi_empty_list_user_restricted);
643             return;
644         }
645         final int wifiState = mWifiManager.getWifiState();
646 
647         //when we update the screen, check if verbose logging has been turned on or off
648         mVerboseLogging = mWifiManager.getVerboseLoggingLevel();
649 
650         switch (wifiState) {
651             case WifiManager.WIFI_STATE_ENABLED:
652                 // AccessPoints are automatically sorted with TreeSet.
653                 final Collection<AccessPoint> accessPoints =
654                         constructAccessPoints(getActivity(), mWifiManager, mLastInfo, mLastState);
655                 getPreferenceScreen().removeAll();
656                 if (accessPoints.size() == 0) {
657                     addMessagePreference(R.string.wifi_empty_list_wifi_on);
658                 }
659 
660                 getListView().removeHeaderView(mWifiAssistantCard);
661                 if (mWifiAssistantApp != null) {
662                     getListView().addHeaderView(mWifiAssistantCard);
663                 }
664 
665                 for (AccessPoint accessPoint : accessPoints) {
666                     // Ignore access points that are out of range.
667                     if (accessPoint.getLevel() != -1) {
668                         getPreferenceScreen().addPreference(accessPoint);
669                     }
670                 }
671                 break;
672 
673             case WifiManager.WIFI_STATE_ENABLING:
674                 getPreferenceScreen().removeAll();
675                 break;
676 
677             case WifiManager.WIFI_STATE_DISABLING:
678                 addMessagePreference(R.string.wifi_stopping);
679                 break;
680 
681             case WifiManager.WIFI_STATE_DISABLED:
682                 setOffMessage();
683                 break;
684         }
685     }
686 
687     /**
688      * Returns the Network Scorer for the Wifi Assistant App.
689      */
getWifiAssistantApp(Context context)690     public static NetworkScorerAppData getWifiAssistantApp(Context context) {
691         Collection<NetworkScorerAppData> scorers =
692                 NetworkScorerAppManager.getAllValidScorers(context);
693 
694         if (scorers.isEmpty()) {
695             return null;
696         }
697 
698         // TODO: b/13780935 - Implement proper scorer selection. Rather than pick the first
699         // scorer on the system, we should allow the user to select one.
700         return scorers.iterator().next();
701     }
702 
prepareWifiAssistantCard()703     private void prepareWifiAssistantCard() {
704         if (getActivity() instanceof WifiPickerActivity) {
705             return;
706         }
707 
708         if (NetworkScorerAppManager.getActiveScorer(getActivity()) != null) {
709             // A scorer is already enabled; don't show the card.
710             return;
711         }
712 
713         Collection<NetworkScorerAppData> scorers =
714                 NetworkScorerAppManager.getAllValidScorers(getActivity());
715         if (scorers.isEmpty()) {
716             // No scorers are available to enable; don't show the card.
717             return;
718         }
719 
720         SharedPreferences sharedPreferences = getPreferenceScreen().getSharedPreferences();
721         int lastDismissPlatform = sharedPreferences.getInt(KEY_ASSISTANT_DISMISS_PLATFORM, 0);
722 
723         if (Build.VERSION.SDK_INT <= lastDismissPlatform) {
724             // User has dismissed the Wi-Fi assistant card on this SDK release. Suppress the card
725             // until the next major platform upgrade.
726             return;
727         }
728 
729         // TODO: b/13780935 - Implement proper scorer selection. Rather than pick the first
730         // scorer on the system, we should allow the user to select one.
731         mWifiAssistantApp = scorers.iterator().next();
732 
733         if (mWifiAssistantCard == null) {
734             mWifiAssistantCard = LayoutInflater.from(getActivity())
735                     .inflate(R.layout.wifi_assistant_card, getListView(), false);
736             Button setup = (Button) mWifiAssistantCard.findViewById(R.id.setup);
737             Button noThanks = (Button) mWifiAssistantCard.findViewById(R.id.no_thanks_button);
738             TextView assistantText =
739                 (TextView) mWifiAssistantCard.findViewById(R.id.wifi_assistant_text);
740             assistantText.setText(getResources().getString(
741                     R.string.wifi_assistant_title_message, mWifiAssistantApp.mScorerName));
742 
743             if (setup != null && noThanks != null) {
744                 setup.setOnClickListener(new OnClickListener() {
745                     @Override
746                     public void onClick(View v) {
747                         Intent intent = new Intent();
748                         if (mWifiAssistantApp.mConfigurationActivityClassName != null) {
749                             // App has a custom configuration activity; launch that.
750                             // This custom activity will be responsible for launching the system
751                             // dialog.
752                             intent.setClassName(mWifiAssistantApp.mPackageName,
753                                     mWifiAssistantApp.mConfigurationActivityClassName);
754                         } else {
755                             // Fall back on the system dialog.
756                             intent.setAction(NetworkScoreManager.ACTION_CHANGE_ACTIVE);
757                             intent.putExtra(NetworkScoreManager.EXTRA_PACKAGE_NAME,
758                                     mWifiAssistantApp.mPackageName);
759                         }
760                         startActivityForResult(intent, REQUEST_ENABLE_WIFI_ASSISTANT);
761                     }
762                 });
763 
764                 noThanks.setOnClickListener(new OnClickListener() {
765                     @Override
766                     public void onClick(View v) {
767                         disableWifiAssistantCardUntilPlatformUpgrade();
768                         getListView().removeHeaderView(mWifiAssistantCard);
769                         mWifiAssistantApp = null;
770                     }
771                 });
772             }
773         }
774     }
775 
disableWifiAssistantCardUntilPlatformUpgrade()776     private void disableWifiAssistantCardUntilPlatformUpgrade() {
777         SharedPreferences sharedPreferences = getPreferenceScreen().getSharedPreferences();
778         SharedPreferences.Editor editor = sharedPreferences.edit();
779         editor.putInt(KEY_ASSISTANT_DISMISS_PLATFORM, Build.VERSION.SDK_INT);
780         editor.apply();
781     }
782 
initEmptyView()783     protected TextView initEmptyView() {
784         TextView emptyView = (TextView) getActivity().findViewById(android.R.id.empty);
785         getListView().setEmptyView(emptyView);
786         return emptyView;
787     }
788 
setOffMessage()789     private void setOffMessage() {
790         if (mEmptyView != null) {
791             mEmptyView.setText(R.string.wifi_empty_list_wifi_off);
792             if (android.provider.Settings.Global.getInt(getActivity().getContentResolver(),
793                     android.provider.Settings.Global.WIFI_SCAN_ALWAYS_AVAILABLE, 0) == 1) {
794                 mEmptyView.append("\n\n");
795                 int resId;
796                 if (android.provider.Settings.Secure.isLocationProviderEnabled(
797                         getActivity().getContentResolver(), LocationManager.NETWORK_PROVIDER)) {
798                     resId = R.string.wifi_scan_notify_text_location_on;
799                 } else {
800                     resId = R.string.wifi_scan_notify_text_location_off;
801                 }
802                 CharSequence charSeq = getText(resId);
803                 mEmptyView.append(charSeq);
804             }
805         }
806         getPreferenceScreen().removeAll();
807     }
808 
addMessagePreference(int messageId)809     private void addMessagePreference(int messageId) {
810         if (mEmptyView != null) mEmptyView.setText(messageId);
811         getPreferenceScreen().removeAll();
812     }
813 
814     /** Returns sorted list of access points */
constructAccessPoints(Context context, WifiManager wifiManager, WifiInfo lastInfo, DetailedState lastState)815     private static List<AccessPoint> constructAccessPoints(Context context,
816             WifiManager wifiManager, WifiInfo lastInfo, DetailedState lastState) {
817         ArrayList<AccessPoint> accessPoints = new ArrayList<AccessPoint>();
818         /** Lookup table to more quickly update AccessPoints by only considering objects with the
819          * correct SSID.  Maps SSID -> List of AccessPoints with the given SSID.  */
820         Multimap<String, AccessPoint> apMap = new Multimap<String, AccessPoint>();
821 
822         final List<WifiConfiguration> configs = wifiManager.getConfiguredNetworks();
823         if (configs != null) {
824             // Update "Saved Networks" menu option.
825             if (savedNetworksExist != (configs.size() > 0)) {
826                 savedNetworksExist = !savedNetworksExist;
827                 if (context instanceof Activity) {
828                     ((Activity) context).invalidateOptionsMenu();
829                 }
830             }
831             for (WifiConfiguration config : configs) {
832                 if (config.selfAdded && config.numAssociation == 0) {
833                     continue;
834                 }
835                 AccessPoint accessPoint = new AccessPoint(context, config);
836                 if (lastInfo != null && lastState != null) {
837                     accessPoint.update(lastInfo, lastState);
838                 }
839                 accessPoints.add(accessPoint);
840                 apMap.put(accessPoint.ssid, accessPoint);
841             }
842         }
843 
844         final List<ScanResult> results = wifiManager.getScanResults();
845         if (results != null) {
846             for (ScanResult result : results) {
847                 // Ignore hidden and ad-hoc networks.
848                 if (result.SSID == null || result.SSID.length() == 0 ||
849                         result.capabilities.contains("[IBSS]")) {
850                     continue;
851                 }
852 
853                 boolean found = false;
854                 for (AccessPoint accessPoint : apMap.getAll(result.SSID)) {
855                     if (accessPoint.update(result))
856                         found = true;
857                 }
858                 if (!found) {
859                     AccessPoint accessPoint = new AccessPoint(context, result);
860                     accessPoints.add(accessPoint);
861                     apMap.put(accessPoint.ssid, accessPoint);
862                 }
863             }
864         }
865 
866         // Pre-sort accessPoints to speed preference insertion
867         Collections.sort(accessPoints);
868         return accessPoints;
869     }
870 
handleEvent(Intent intent)871     private void handleEvent(Intent intent) {
872         String action = intent.getAction();
873         if (WifiManager.WIFI_STATE_CHANGED_ACTION.equals(action)) {
874             updateWifiState(intent.getIntExtra(WifiManager.EXTRA_WIFI_STATE,
875                     WifiManager.WIFI_STATE_UNKNOWN));
876         } else if (WifiManager.SCAN_RESULTS_AVAILABLE_ACTION.equals(action) ||
877                 WifiManager.CONFIGURED_NETWORKS_CHANGED_ACTION.equals(action) ||
878                 WifiManager.LINK_CONFIGURATION_CHANGED_ACTION.equals(action)) {
879                 updateAccessPoints();
880         } else if (WifiManager.NETWORK_STATE_CHANGED_ACTION.equals(action)) {
881             NetworkInfo info = (NetworkInfo) intent.getParcelableExtra(
882                     WifiManager.EXTRA_NETWORK_INFO);
883             mConnected.set(info.isConnected());
884             changeNextButtonState(info.isConnected());
885             updateAccessPoints();
886             updateConnectionState(info.getDetailedState());
887         } else if (WifiManager.RSSI_CHANGED_ACTION.equals(action)) {
888             updateConnectionState(null);
889         }
890     }
891 
updateConnectionState(DetailedState state)892     private void updateConnectionState(DetailedState state) {
893         /* sticky broadcasts can call this when wifi is disabled */
894         if (!mWifiManager.isWifiEnabled()) {
895             mScanner.pause();
896             return;
897         }
898 
899         if (state == DetailedState.OBTAINING_IPADDR) {
900             mScanner.pause();
901         } else {
902             mScanner.resume();
903         }
904 
905         mLastInfo = mWifiManager.getConnectionInfo();
906         if (state != null) {
907             mLastState = state;
908         }
909 
910         for (int i = getPreferenceScreen().getPreferenceCount() - 1; i >= 0; --i) {
911             // Maybe there's a WifiConfigPreference
912             Preference preference = getPreferenceScreen().getPreference(i);
913             if (preference instanceof AccessPoint) {
914                 final AccessPoint accessPoint = (AccessPoint) preference;
915                 accessPoint.update(mLastInfo, mLastState);
916             }
917         }
918     }
919 
updateWifiState(int state)920     private void updateWifiState(int state) {
921         Activity activity = getActivity();
922         if (activity != null) {
923             activity.invalidateOptionsMenu();
924         }
925 
926         switch (state) {
927             case WifiManager.WIFI_STATE_ENABLED:
928                 mScanner.resume();
929                 return; // not break, to avoid the call to pause() below
930 
931             case WifiManager.WIFI_STATE_ENABLING:
932                 addMessagePreference(R.string.wifi_starting);
933                 break;
934 
935             case WifiManager.WIFI_STATE_DISABLED:
936                 setOffMessage();
937                 break;
938         }
939 
940         mLastInfo = null;
941         mLastState = null;
942         mScanner.pause();
943     }
944 
945     /**
946      * Renames/replaces "Next" button when appropriate. "Next" button usually exists in
947      * Wifi setup screens, not in usual wifi settings screen.
948      *
949      * @param enabled true when the device is connected to a wifi network.
950      */
changeNextButtonState(boolean enabled)951     private void changeNextButtonState(boolean enabled) {
952         if (mEnableNextOnConnection && hasNextButton()) {
953             getNextButton().setEnabled(enabled);
954         }
955     }
956 
957     @Override
onClick(DialogInterface dialogInterface, int button)958     public void onClick(DialogInterface dialogInterface, int button) {
959         if (button == WifiDialog.BUTTON_FORGET && mSelectedAccessPoint != null) {
960             forget();
961         } else if (button == WifiDialog.BUTTON_SUBMIT) {
962             if (mDialog != null) {
963                 submit(mDialog.getController());
964             }
965         }
966     }
967 
submit(WifiConfigController configController)968     /* package */ void submit(WifiConfigController configController) {
969 
970         final WifiConfiguration config = configController.getConfig();
971 
972         if (config == null) {
973             if (mSelectedAccessPoint != null
974                     && mSelectedAccessPoint.networkId != INVALID_NETWORK_ID) {
975                 connect(mSelectedAccessPoint.networkId);
976             }
977         } else if (config.networkId != INVALID_NETWORK_ID) {
978             if (mSelectedAccessPoint != null) {
979                 mWifiManager.save(config, mSaveListener);
980             }
981         } else {
982             if (configController.isEdit()) {
983                 mWifiManager.save(config, mSaveListener);
984             } else {
985                 connect(config);
986             }
987         }
988 
989         if (mWifiManager.isWifiEnabled()) {
990             mScanner.resume();
991         }
992         updateAccessPoints();
993     }
994 
forget()995     /* package */ void forget() {
996         if (mSelectedAccessPoint.networkId == INVALID_NETWORK_ID) {
997             // Should not happen, but a monkey seems to trigger it
998             Log.e(TAG, "Failed to forget invalid network " + mSelectedAccessPoint.getConfig());
999             return;
1000         }
1001 
1002         mWifiManager.forget(mSelectedAccessPoint.networkId, mForgetListener);
1003 
1004         if (mWifiManager.isWifiEnabled()) {
1005             mScanner.resume();
1006         }
1007         updateAccessPoints();
1008 
1009         // We need to rename/replace "Next" button in wifi setup context.
1010         changeNextButtonState(false);
1011     }
1012 
connect(final WifiConfiguration config)1013     protected void connect(final WifiConfiguration config) {
1014         mWifiManager.connect(config, mConnectListener);
1015     }
1016 
connect(final int networkId)1017     protected void connect(final int networkId) {
1018         mWifiManager.connect(networkId, mConnectListener);
1019     }
1020 
1021     /**
1022      * Refreshes acccess points and ask Wifi module to scan networks again.
1023      */
refreshAccessPoints()1024     /* package */ void refreshAccessPoints() {
1025         if (mWifiManager.isWifiEnabled()) {
1026             mScanner.resume();
1027         }
1028 
1029         getPreferenceScreen().removeAll();
1030     }
1031 
1032     /**
1033      * Called when "add network" button is pressed.
1034      */
onAddNetworkPressed()1035     /* package */ void onAddNetworkPressed() {
1036         // No exact access point is selected.
1037         mSelectedAccessPoint = null;
1038         showDialog(null, true);
1039     }
1040 
getAccessPointsCount()1041     /* package */ int getAccessPointsCount() {
1042         final boolean wifiIsEnabled = mWifiManager.isWifiEnabled();
1043         if (wifiIsEnabled) {
1044             return getPreferenceScreen().getPreferenceCount();
1045         } else {
1046             return 0;
1047         }
1048     }
1049 
1050     /**
1051      * Requests wifi module to pause wifi scan. May be ignored when the module is disabled.
1052      */
pauseWifiScan()1053     /* package */ void pauseWifiScan() {
1054         if (mWifiManager.isWifiEnabled()) {
1055             mScanner.pause();
1056         }
1057     }
1058 
1059     /**
1060      * Requests wifi module to resume wifi scan. May be ignored when the module is disabled.
1061      */
resumeWifiScan()1062     /* package */ void resumeWifiScan() {
1063         if (mWifiManager.isWifiEnabled()) {
1064             mScanner.resume();
1065         }
1066     }
1067 
1068     @Override
getHelpResource()1069     protected int getHelpResource() {
1070         return R.string.help_url_wifi;
1071     }
1072 
1073     public static final SearchIndexProvider SEARCH_INDEX_DATA_PROVIDER =
1074         new BaseSearchIndexProvider() {
1075             @Override
1076             public List<SearchIndexableRaw> getRawDataToIndex(Context context, boolean enabled) {
1077                 final List<SearchIndexableRaw> result = new ArrayList<SearchIndexableRaw>();
1078                 final Resources res = context.getResources();
1079 
1080                 // Add fragment title
1081                 SearchIndexableRaw data = new SearchIndexableRaw(context);
1082                 data.title = res.getString(R.string.wifi_settings);
1083                 data.screenTitle = res.getString(R.string.wifi_settings);
1084                 data.keywords = res.getString(R.string.keywords_wifi);
1085                 result.add(data);
1086 
1087                 // Add available Wi-Fi access points
1088                 WifiManager wifiManager =
1089                         (WifiManager) context.getSystemService(Context.WIFI_SERVICE);
1090                 final Collection<AccessPoint> accessPoints =
1091                         constructAccessPoints(context, wifiManager, null, null);
1092                 for (AccessPoint accessPoint : accessPoints) {
1093                     // We are indexing only the saved Wi-Fi networks.
1094                     if (accessPoint.getConfig() == null) continue;
1095                     data = new SearchIndexableRaw(context);
1096                     data.title = accessPoint.getTitle().toString();
1097                     data.screenTitle = res.getString(R.string.wifi_settings);
1098                     data.enabled = enabled;
1099                     result.add(data);
1100                 }
1101 
1102                 return result;
1103             }
1104         };
1105 }
1106