WifiSettings.java revision 088537a315b74cdcd3ab3201c8de2c79417ebae4
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
17package com.android.settings.wifi;
18
19import static android.net.wifi.WifiConfiguration.INVALID_NETWORK_ID;
20import static android.os.UserManager.DISALLOW_CONFIG_WIFI;
21
22import android.app.Activity;
23import android.app.AlertDialog;
24import android.app.AppGlobals;
25import android.app.Dialog;
26import android.app.admin.DeviceAdminInfo;
27import android.app.admin.DevicePolicyManager;
28import android.app.admin.DevicePolicyManagerInternal;
29import android.content.ContentResolver;
30import android.content.Context;
31import android.content.DialogInterface;
32import android.content.Intent;
33import android.content.pm.ApplicationInfo;
34import android.content.pm.IPackageManager;
35import android.content.pm.PackageManager;
36import android.content.pm.PackageManager.NameNotFoundException;
37import android.content.res.Resources;
38import android.content.res.TypedArray;
39import android.net.ConnectivityManager;
40import android.net.NetworkInfo;
41import android.net.NetworkInfo.State;
42import android.net.wifi.WifiConfiguration;
43import android.net.wifi.WifiManager;
44import android.net.wifi.WpsInfo;
45import android.nfc.NfcAdapter;
46import android.os.Bundle;
47import android.os.HandlerThread;
48import android.os.Process;
49import android.os.RemoteException;
50import android.os.UserHandle;
51import android.os.UserManager;
52import android.preference.Preference;
53import android.preference.PreferenceScreen;
54import android.provider.Settings;
55import android.text.Spannable;
56import android.text.style.TextAppearanceSpan;
57import android.util.Log;
58import android.view.ContextMenu;
59import android.view.ContextMenu.ContextMenuInfo;
60import android.view.Gravity;
61import android.view.Menu;
62import android.view.MenuInflater;
63import android.view.MenuItem;
64import android.view.View;
65import android.widget.AdapterView.AdapterContextMenuInfo;
66import android.widget.ProgressBar;
67import android.widget.TextView;
68import android.widget.TextView.BufferType;
69import android.widget.Toast;
70
71import com.android.internal.logging.MetricsLogger;
72import com.android.server.LocalServices;
73import com.android.settings.LinkifyUtils;
74import com.android.settings.R;
75import com.android.settings.RestrictedSettingsFragment;
76import com.android.settings.SettingsActivity;
77import com.android.settings.location.ScanningSettings;
78import com.android.settings.search.BaseSearchIndexProvider;
79import com.android.settings.search.Indexable;
80import com.android.settings.search.SearchIndexableRaw;
81import com.android.settings.wifi.AccessPointPreference.UserBadgeCache;
82import com.android.settingslib.wifi.AccessPoint;
83import com.android.settingslib.wifi.AccessPoint.AccessPointListener;
84import com.android.settingslib.wifi.WifiTracker;
85
86import java.util.ArrayList;
87import java.util.Collection;
88import java.util.List;
89
90/**
91 * Two types of UI are provided here.
92 *
93 * The first is for "usual Settings", appearing as any other Setup fragment.
94 *
95 * The second is for Setup Wizard, with a simplified interface that hides the action bar
96 * and menus.
97 */
98public class WifiSettings extends RestrictedSettingsFragment
99        implements DialogInterface.OnClickListener, Indexable, WifiTracker.WifiListener,
100        AccessPointListener {
101
102    private static final String TAG = "WifiSettings";
103
104    /* package */ static final int MENU_ID_WPS_PBC = Menu.FIRST;
105    private static final int MENU_ID_WPS_PIN = Menu.FIRST + 1;
106    private static final int MENU_ID_SAVED_NETWORK = Menu.FIRST + 2;
107    /* package */ static final int MENU_ID_ADD_NETWORK = Menu.FIRST + 3;
108    private static final int MENU_ID_ADVANCED = Menu.FIRST + 4;
109    private static final int MENU_ID_SCAN = Menu.FIRST + 5;
110    private static final int MENU_ID_CONNECT = Menu.FIRST + 6;
111    private static final int MENU_ID_FORGET = Menu.FIRST + 7;
112    private static final int MENU_ID_MODIFY = Menu.FIRST + 8;
113    private static final int MENU_ID_WRITE_NFC = Menu.FIRST + 9;
114
115    public static final int WIFI_DIALOG_ID = 1;
116    /* package */ static final int WPS_PBC_DIALOG_ID = 2;
117    private static final int WPS_PIN_DIALOG_ID = 3;
118    private static final int WRITE_NFC_DIALOG_ID = 6;
119
120    // Instance state keys
121    private static final String SAVE_DIALOG_EDIT_MODE = "edit_mode";
122    private static final String SAVE_DIALOG_ACCESS_POINT_STATE = "wifi_ap_state";
123    private static final String SAVED_WIFI_NFC_DIALOG_STATE = "wifi_nfc_dlg_state";
124
125    private static boolean savedNetworksExist;
126
127    protected WifiManager mWifiManager;
128    private WifiManager.ActionListener mConnectListener;
129    private WifiManager.ActionListener mSaveListener;
130    private WifiManager.ActionListener mForgetListener;
131
132    private WifiEnabler mWifiEnabler;
133    // An access point being editted is stored here.
134    private AccessPoint mSelectedAccessPoint;
135
136    private WifiDialog mDialog;
137    private WriteWifiConfigToNfcDialog mWifiToNfcDialog;
138
139    private TextView mEmptyView;
140    private ProgressBar mProgressHeader;
141
142    // this boolean extra specifies whether to disable the Next button when not connected. Used by
143    // account creation outside of setup wizard.
144    private static final String EXTRA_ENABLE_NEXT_ON_CONNECT = "wifi_enable_next_on_connect";
145    // This string extra specifies a network to open the connect dialog on, so the user can enter
146    // network credentials.  This is used by quick settings for secured networks.
147    private static final String EXTRA_START_CONNECT_SSID = "wifi_start_connect_ssid";
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 Bundle mWifiNfcDialogSavedState;
157
158    private WifiTracker mWifiTracker;
159    private String mOpenSsid;
160
161    private HandlerThread mBgThread;
162
163    private UserBadgeCache mUserBadgeCache;
164
165    /* End of "used in Wifi Setup context" */
166
167    public WifiSettings() {
168        super(DISALLOW_CONFIG_WIFI);
169    }
170
171    @Override
172    public void onViewCreated(View view, Bundle savedInstanceState) {
173        super.onViewCreated(view, savedInstanceState);
174        final Activity activity = getActivity();
175        if (activity != null) {
176            mProgressHeader = (ProgressBar) setPinnedHeaderView(R.layout.wifi_progress_header);
177        }
178    }
179
180    @Override
181    public void onCreate(Bundle icicle) {
182        super.onCreate(icicle);
183        addPreferencesFromResource(R.xml.wifi_settings);
184        mUserBadgeCache = new UserBadgeCache(getPackageManager());
185
186        mBgThread = new HandlerThread(TAG, Process.THREAD_PRIORITY_BACKGROUND);
187        mBgThread.start();
188    }
189
190    @Override
191    public void onDestroy() {
192        mBgThread.quit();
193        super.onDestroy();
194    }
195
196    @Override
197    public void onActivityCreated(Bundle savedInstanceState) {
198        super.onActivityCreated(savedInstanceState);
199
200        mWifiTracker =
201                new WifiTracker(getActivity(), this, mBgThread.getLooper(), true, true, false);
202        mWifiManager = mWifiTracker.getManager();
203
204        mConnectListener = new WifiManager.ActionListener() {
205                                   @Override
206                                   public void onSuccess() {
207                                   }
208                                   @Override
209                                   public void onFailure(int reason) {
210                                       Activity activity = getActivity();
211                                       if (activity != null) {
212                                           Toast.makeText(activity,
213                                                R.string.wifi_failed_connect_message,
214                                                Toast.LENGTH_SHORT).show();
215                                       }
216                                   }
217                               };
218
219        mSaveListener = new WifiManager.ActionListener() {
220                                @Override
221                                public void onSuccess() {
222                                }
223                                @Override
224                                public void onFailure(int reason) {
225                                    Activity activity = getActivity();
226                                    if (activity != null) {
227                                        Toast.makeText(activity,
228                                            R.string.wifi_failed_save_message,
229                                            Toast.LENGTH_SHORT).show();
230                                    }
231                                }
232                            };
233
234        mForgetListener = new WifiManager.ActionListener() {
235                                   @Override
236                                   public void onSuccess() {
237                                   }
238                                   @Override
239                                   public void onFailure(int reason) {
240                                       Activity activity = getActivity();
241                                       if (activity != null) {
242                                           Toast.makeText(activity,
243                                               R.string.wifi_failed_forget_message,
244                                               Toast.LENGTH_SHORT).show();
245                                       }
246                                   }
247                               };
248
249        if (savedInstanceState != null) {
250            mDlgEdit = savedInstanceState.getBoolean(SAVE_DIALOG_EDIT_MODE);
251            if (savedInstanceState.containsKey(SAVE_DIALOG_ACCESS_POINT_STATE)) {
252                mAccessPointSavedState =
253                    savedInstanceState.getBundle(SAVE_DIALOG_ACCESS_POINT_STATE);
254            }
255
256            if (savedInstanceState.containsKey(SAVED_WIFI_NFC_DIALOG_STATE)) {
257                mWifiNfcDialogSavedState =
258                    savedInstanceState.getBundle(SAVED_WIFI_NFC_DIALOG_STATE);
259            }
260        }
261
262        // if we're supposed to enable/disable the Next button based on our current connection
263        // state, start it off in the right state
264        Intent intent = getActivity().getIntent();
265        mEnableNextOnConnection = intent.getBooleanExtra(EXTRA_ENABLE_NEXT_ON_CONNECT, false);
266
267        if (mEnableNextOnConnection) {
268            if (hasNextButton()) {
269                final ConnectivityManager connectivity = (ConnectivityManager)
270                        getActivity().getSystemService(Context.CONNECTIVITY_SERVICE);
271                if (connectivity != null) {
272                    NetworkInfo info = connectivity.getNetworkInfo(
273                            ConnectivityManager.TYPE_WIFI);
274                    changeNextButtonState(info.isConnected());
275                }
276            }
277        }
278
279        mEmptyView = initEmptyView();
280        registerForContextMenu(getListView());
281        setHasOptionsMenu(true);
282
283        if (intent.hasExtra(EXTRA_START_CONNECT_SSID)) {
284            mOpenSsid = intent.getStringExtra(EXTRA_START_CONNECT_SSID);
285            onAccessPointsChanged();
286        }
287    }
288
289    @Override
290    public void onDestroyView() {
291        super.onDestroyView();
292
293        if (mWifiEnabler != null) {
294            mWifiEnabler.teardownSwitchBar();
295        }
296    }
297
298    @Override
299    public void onStart() {
300        super.onStart();
301
302        // On/off switch is hidden for Setup Wizard (returns null)
303        mWifiEnabler = createWifiEnabler();
304    }
305
306    /**
307     * @return new WifiEnabler or null (as overridden by WifiSettingsForSetupWizard)
308     */
309    /* package */ WifiEnabler createWifiEnabler() {
310        final SettingsActivity activity = (SettingsActivity) getActivity();
311        return new WifiEnabler(activity, activity.getSwitchBar());
312    }
313
314    @Override
315    public void onResume() {
316        final Activity activity = getActivity();
317        super.onResume();
318        removePreference("dummy");
319        if (mWifiEnabler != null) {
320            mWifiEnabler.resume(activity);
321        }
322
323        mWifiTracker.startTracking();
324    }
325
326    @Override
327    public void onPause() {
328        super.onPause();
329        if (mWifiEnabler != null) {
330            mWifiEnabler.pause();
331        }
332
333        mWifiTracker.stopTracking();
334    }
335
336    @Override
337    public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
338        // If the user is not allowed to configure wifi, do not show the menu.
339        if (isUiRestricted()) return;
340
341        addOptionsMenuItems(menu);
342        super.onCreateOptionsMenu(menu, inflater);
343    }
344
345    /**
346     * @param menu
347     */
348    void addOptionsMenuItems(Menu menu) {
349        final boolean wifiIsEnabled = mWifiTracker.isWifiEnabled();
350        TypedArray ta = getActivity().getTheme().obtainStyledAttributes(
351                new int[] {R.attr.ic_menu_add, R.attr.ic_wps});
352        menu.add(Menu.NONE, MENU_ID_ADD_NETWORK, 0, R.string.wifi_add_network)
353                .setIcon(ta.getDrawable(0))
354                .setEnabled(wifiIsEnabled)
355                .setShowAsAction(MenuItem.SHOW_AS_ACTION_NEVER);
356        if (savedNetworksExist) {
357            menu.add(Menu.NONE, MENU_ID_SAVED_NETWORK, 0, R.string.wifi_saved_access_points_label)
358                    .setIcon(ta.getDrawable(0))
359                    .setEnabled(wifiIsEnabled)
360                    .setShowAsAction(MenuItem.SHOW_AS_ACTION_NEVER);
361        }
362        menu.add(Menu.NONE, MENU_ID_SCAN, 0, R.string.menu_stats_refresh)
363               .setEnabled(wifiIsEnabled)
364               .setShowAsAction(MenuItem.SHOW_AS_ACTION_NEVER);
365        menu.add(Menu.NONE, MENU_ID_ADVANCED, 0, R.string.wifi_menu_advanced)
366                .setShowAsAction(MenuItem.SHOW_AS_ACTION_NEVER);
367        ta.recycle();
368    }
369
370    @Override
371    protected int getMetricsCategory() {
372        return MetricsLogger.WIFI;
373    }
374
375    @Override
376    public void onSaveInstanceState(Bundle outState) {
377        super.onSaveInstanceState(outState);
378
379        // If the dialog is showing, save its state.
380        if (mDialog != null && mDialog.isShowing()) {
381            outState.putBoolean(SAVE_DIALOG_EDIT_MODE, mDlgEdit);
382            if (mDlgAccessPoint != null) {
383                mAccessPointSavedState = new Bundle();
384                mDlgAccessPoint.saveWifiState(mAccessPointSavedState);
385                outState.putBundle(SAVE_DIALOG_ACCESS_POINT_STATE, mAccessPointSavedState);
386            }
387        }
388
389        if (mWifiToNfcDialog != null && mWifiToNfcDialog.isShowing()) {
390            Bundle savedState = new Bundle();
391            mWifiToNfcDialog.saveState(savedState);
392            outState.putBundle(SAVED_WIFI_NFC_DIALOG_STATE, savedState);
393        }
394    }
395
396    @Override
397    public boolean onOptionsItemSelected(MenuItem item) {
398        // If the user is not allowed to configure wifi, do not handle menu selections.
399        if (isUiRestricted()) return false;
400
401        switch (item.getItemId()) {
402            case MENU_ID_WPS_PBC:
403                showDialog(WPS_PBC_DIALOG_ID);
404                return true;
405                /*
406            case MENU_ID_P2P:
407                if (getActivity() instanceof SettingsActivity) {
408                    ((SettingsActivity) getActivity()).startPreferencePanel(
409                            WifiP2pSettings.class.getCanonicalName(),
410                            null,
411                            R.string.wifi_p2p_settings_title, null,
412                            this, 0);
413                } else {
414                    startFragment(this, WifiP2pSettings.class.getCanonicalName(),
415                            R.string.wifi_p2p_settings_title, -1, null);
416                }
417                return true;
418                */
419            case MENU_ID_WPS_PIN:
420                showDialog(WPS_PIN_DIALOG_ID);
421                return true;
422            case MENU_ID_SCAN:
423                MetricsLogger.action(getActivity(), MetricsLogger.ACTION_WIFI_FORCE_SCAN);
424                mWifiTracker.forceScan();
425                return true;
426            case MENU_ID_ADD_NETWORK:
427                if (mWifiTracker.isWifiEnabled()) {
428                    onAddNetworkPressed();
429                }
430                return true;
431            case MENU_ID_SAVED_NETWORK:
432                if (getActivity() instanceof SettingsActivity) {
433                    ((SettingsActivity) getActivity()).startPreferencePanel(
434                            SavedAccessPointsWifiSettings.class.getCanonicalName(), null,
435                            R.string.wifi_saved_access_points_titlebar, null, this, 0);
436                } else {
437                    startFragment(this, SavedAccessPointsWifiSettings.class.getCanonicalName(),
438                            R.string.wifi_saved_access_points_titlebar,
439                            -1 /* Do not request a result */, null);
440                }
441                return true;
442            case MENU_ID_ADVANCED:
443                if (getActivity() instanceof SettingsActivity) {
444                    ((SettingsActivity) getActivity()).startPreferencePanel(
445                            AdvancedWifiSettings.class.getCanonicalName(), null,
446                            R.string.wifi_advanced_titlebar, null, this, 0);
447                } else {
448                    startFragment(this, AdvancedWifiSettings.class.getCanonicalName(),
449                            R.string.wifi_advanced_titlebar, -1 /* Do not request a results */,
450                            null);
451                }
452                return true;
453        }
454        return super.onOptionsItemSelected(item);
455    }
456
457    @Override
458    public void onCreateContextMenu(ContextMenu menu, View view, ContextMenuInfo info) {
459        if (info instanceof AdapterContextMenuInfo) {
460            Preference preference = (Preference) getListView().getItemAtPosition(
461                    ((AdapterContextMenuInfo) info).position);
462
463            if (preference instanceof AccessPointPreference) {
464                mSelectedAccessPoint = ((AccessPointPreference) preference).getAccessPoint();
465                menu.setHeaderTitle(mSelectedAccessPoint.getSsid());
466                if (mSelectedAccessPoint.isConnectable()) {
467                    menu.add(Menu.NONE, MENU_ID_CONNECT, 0, R.string.wifi_menu_connect);
468                }
469
470                WifiConfiguration config = mSelectedAccessPoint.getConfig();
471                // Some configs are ineditable
472                if (isEditabilityLockedDown(getActivity(), config)) {
473                    return;
474                }
475
476                if (mSelectedAccessPoint.isSaved() || mSelectedAccessPoint.isEphemeral()) {
477                    // Allow forgetting a network if either the network is saved or ephemerally
478                    // connected. (In the latter case, "forget" blacklists the network so it won't
479                    // be used again, ephemerally).
480                    menu.add(Menu.NONE, MENU_ID_FORGET, 0, R.string.wifi_menu_forget);
481                }
482                if (mSelectedAccessPoint.isSaved()) {
483                    menu.add(Menu.NONE, MENU_ID_MODIFY, 0, R.string.wifi_menu_modify);
484                    NfcAdapter nfcAdapter = NfcAdapter.getDefaultAdapter(getActivity());
485                    if (nfcAdapter != null && nfcAdapter.isEnabled() &&
486                            mSelectedAccessPoint.getSecurity() != AccessPoint.SECURITY_NONE) {
487                        // Only allow writing of NFC tags for password-protected networks.
488                        menu.add(Menu.NONE, MENU_ID_WRITE_NFC, 0, R.string.wifi_menu_write_to_nfc);
489                    }
490                }
491            }
492        }
493    }
494
495    @Override
496    public boolean onContextItemSelected(MenuItem item) {
497        if (mSelectedAccessPoint == null) {
498            return super.onContextItemSelected(item);
499        }
500        switch (item.getItemId()) {
501            case MENU_ID_CONNECT: {
502                if (mSelectedAccessPoint.isSaved()) {
503                    connect(mSelectedAccessPoint.getConfig());
504                } else if (mSelectedAccessPoint.getSecurity() == AccessPoint.SECURITY_NONE) {
505                    /** Bypass dialog for unsecured networks */
506                    mSelectedAccessPoint.generateOpenNetworkConfig();
507                    connect(mSelectedAccessPoint.getConfig());
508                } else {
509                    showDialog(mSelectedAccessPoint, true);
510                }
511                return true;
512            }
513            case MENU_ID_FORGET: {
514                forget();
515                return true;
516            }
517            case MENU_ID_MODIFY: {
518                showDialog(mSelectedAccessPoint, true);
519                return true;
520            }
521            case MENU_ID_WRITE_NFC:
522                showDialog(WRITE_NFC_DIALOG_ID);
523                return true;
524
525        }
526        return super.onContextItemSelected(item);
527    }
528
529    @Override
530    public boolean onPreferenceTreeClick(PreferenceScreen screen, Preference preference) {
531        if (preference instanceof AccessPointPreference) {
532            mSelectedAccessPoint = ((AccessPointPreference) preference).getAccessPoint();
533            /** Bypass dialog for unsecured, unsaved, and inactive networks */
534            if (mSelectedAccessPoint.getSecurity() == AccessPoint.SECURITY_NONE &&
535                    !mSelectedAccessPoint.isSaved() && !mSelectedAccessPoint.isActive()) {
536                mSelectedAccessPoint.generateOpenNetworkConfig();
537                if (!savedNetworksExist) {
538                    savedNetworksExist = true;
539                    getActivity().invalidateOptionsMenu();
540                }
541                connect(mSelectedAccessPoint.getConfig());
542            } else {
543                showDialog(mSelectedAccessPoint, false);
544            }
545        } else {
546            return super.onPreferenceTreeClick(screen, preference);
547        }
548        return true;
549    }
550
551    private void showDialog(AccessPoint accessPoint, boolean edit) {
552        if (accessPoint != null) {
553            WifiConfiguration config = accessPoint.getConfig();
554            if (isEditabilityLockedDown(getActivity(), config) && accessPoint.isActive()) {
555                final int userId = UserHandle.getUserId(config.creatorUid);
556                final PackageManager pm = getActivity().getPackageManager();
557                final IPackageManager ipm = AppGlobals.getPackageManager();
558                String appName = pm.getNameForUid(config.creatorUid);
559                try {
560                    final ApplicationInfo appInfo = ipm.getApplicationInfo(appName, /* flags */ 0,
561                            userId);
562                    final CharSequence label = pm.getApplicationLabel(appInfo);
563                    if (label != null) {
564                        appName = label.toString();
565                    }
566                } catch (RemoteException e) {
567                    // leave appName as packageName
568                }
569                final AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
570                builder.setTitle(accessPoint.getSsid())
571                        .setMessage(getString(R.string.wifi_alert_lockdown_by_device_owner,
572                                appName))
573                        .setPositiveButton(android.R.string.ok, null)
574                        .show();
575                return;
576            }
577        }
578
579        if (mDialog != null) {
580            removeDialog(WIFI_DIALOG_ID);
581            mDialog = null;
582        }
583
584        // Save the access point and edit mode
585        mDlgAccessPoint = accessPoint;
586        mDlgEdit = edit;
587
588        showDialog(WIFI_DIALOG_ID);
589    }
590
591    @Override
592    public Dialog onCreateDialog(int dialogId) {
593        switch (dialogId) {
594            case WIFI_DIALOG_ID:
595                AccessPoint ap = mDlgAccessPoint; // For manual launch
596                if (ap == null) { // For re-launch from saved state
597                    if (mAccessPointSavedState != null) {
598                        ap = new AccessPoint(getActivity(), mAccessPointSavedState);
599                        // For repeated orientation changes
600                        mDlgAccessPoint = ap;
601                        // Reset the saved access point data
602                        mAccessPointSavedState = null;
603                    }
604                }
605                // If it's null, fine, it's for Add Network
606                mSelectedAccessPoint = ap;
607                final boolean hideForget = (ap == null || isEditabilityLockedDown(getActivity(),
608                        ap.getConfig()));
609                mDialog = new WifiDialog(getActivity(), this, ap, mDlgEdit,
610                        /* no hide submit/connect */ false,
611                        /* hide forget if config locked down */ hideForget);
612                return mDialog;
613            case WPS_PBC_DIALOG_ID:
614                return new WpsDialog(getActivity(), WpsInfo.PBC);
615            case WPS_PIN_DIALOG_ID:
616                return new WpsDialog(getActivity(), WpsInfo.DISPLAY);
617            case WRITE_NFC_DIALOG_ID:
618                if (mSelectedAccessPoint != null) {
619                    mWifiToNfcDialog = new WriteWifiConfigToNfcDialog(
620                            getActivity(), mSelectedAccessPoint.getConfig().networkId,
621                            mSelectedAccessPoint.getSecurity(),
622                            mWifiManager);
623                } else if (mWifiNfcDialogSavedState != null) {
624                    mWifiToNfcDialog = new WriteWifiConfigToNfcDialog(
625                            getActivity(), mWifiNfcDialogSavedState, mWifiManager);
626                }
627
628                return mWifiToNfcDialog;
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     */
637    @Override
638    public void onAccessPointsChanged() {
639        // Safeguard from some delayed event handling
640        if (getActivity() == null) return;
641
642        if (isUiRestricted()) {
643            addMessagePreference(R.string.wifi_empty_list_user_restricted);
644            return;
645        }
646        final int wifiState = mWifiManager.getWifiState();
647
648        switch (wifiState) {
649            case WifiManager.WIFI_STATE_ENABLED:
650                // AccessPoints are automatically sorted with TreeSet.
651                final Collection<AccessPoint> accessPoints =
652                        mWifiTracker.getAccessPoints();
653                getPreferenceScreen().removeAll();
654
655                boolean hasAvailableAccessPoints = false;
656                int index = 0;
657                for (AccessPoint accessPoint : accessPoints) {
658                    // Ignore access points that are out of range.
659                    if (accessPoint.getLevel() != -1) {
660                        hasAvailableAccessPoints = true;
661                        if (accessPoint.getTag() != null) {
662                            final Preference pref = (Preference) accessPoint.getTag();
663                            pref.setOrder(index++);
664                            getPreferenceScreen().addPreference(pref);
665                            continue;
666                        }
667                        AccessPointPreference preference = new AccessPointPreference(accessPoint,
668                                getActivity(), mUserBadgeCache, false);
669                        preference.setOrder(index++);
670
671                        if (mOpenSsid != null && mOpenSsid.equals(accessPoint.getSsidStr())
672                                && !accessPoint.isSaved()
673                                && accessPoint.getSecurity() != AccessPoint.SECURITY_NONE) {
674                            onPreferenceTreeClick(getPreferenceScreen(), preference);
675                            mOpenSsid = null;
676                        }
677                        getPreferenceScreen().addPreference(preference);
678                        accessPoint.setListener(this);
679                    }
680                }
681                if (!hasAvailableAccessPoints) {
682                    setProgressBarVisible(true);
683                    addMessagePreference(R.string.wifi_empty_list_wifi_on);
684                } else {
685                    setProgressBarVisible(false);
686                }
687                break;
688
689            case WifiManager.WIFI_STATE_ENABLING:
690                getPreferenceScreen().removeAll();
691                setProgressBarVisible(true);
692                break;
693
694            case WifiManager.WIFI_STATE_DISABLING:
695                addMessagePreference(R.string.wifi_stopping);
696                setProgressBarVisible(true);
697                break;
698
699            case WifiManager.WIFI_STATE_DISABLED:
700                setOffMessage();
701                setProgressBarVisible(false);
702                break;
703        }
704        // Update "Saved Networks" menu option.
705        if (savedNetworksExist != mWifiTracker.doSavedNetworksExist()) {
706            savedNetworksExist = !savedNetworksExist;
707            getActivity().invalidateOptionsMenu();
708        }
709    }
710
711    protected TextView initEmptyView() {
712        TextView emptyView = (TextView) getActivity().findViewById(android.R.id.empty);
713        emptyView.setGravity(Gravity.START | Gravity.CENTER_VERTICAL);
714        getListView().setEmptyView(emptyView);
715        return emptyView;
716    }
717
718    private void setOffMessage() {
719        if (mEmptyView == null) {
720            return;
721        }
722
723        final CharSequence briefText = getText(R.string.wifi_empty_list_wifi_off);
724
725        // Don't use WifiManager.isScanAlwaysAvailable() to check the Wi-Fi scanning mode. Instead,
726        // read the system settings directly. Because when the device is in Airplane mode, even if
727        // Wi-Fi scanning mode is on, WifiManager.isScanAlwaysAvailable() still returns "off".
728        final ContentResolver resolver = getActivity().getContentResolver();
729        final boolean wifiScanningMode = Settings.Global.getInt(
730                resolver, Settings.Global.WIFI_SCAN_ALWAYS_AVAILABLE, 0) == 1;
731
732        if (isUiRestricted() || !wifiScanningMode) {
733            // Show only the brief text if the user is not allowed to configure scanning settings,
734            // or the scanning mode has been turned off.
735            mEmptyView.setText(briefText, BufferType.SPANNABLE);
736        } else {
737            // Append the description of scanning settings with link.
738            final StringBuilder contentBuilder = new StringBuilder();
739            contentBuilder.append(briefText);
740            contentBuilder.append("\n\n");
741            contentBuilder.append(getText(R.string.wifi_scan_notify_text));
742            LinkifyUtils.linkify(mEmptyView, contentBuilder, new LinkifyUtils.OnClickListener() {
743                @Override
744                public void onClick() {
745                    final SettingsActivity activity =
746                            (SettingsActivity) WifiSettings.this.getActivity();
747                    activity.startPreferencePanel(ScanningSettings.class.getName(), null,
748                            R.string.location_scanning_screen_title, null, null, 0);
749                }
750            });
751        }
752        // Embolden and enlarge the brief description anyway.
753        Spannable boldSpan = (Spannable) mEmptyView.getText();
754        boldSpan.setSpan(
755                new TextAppearanceSpan(getActivity(), android.R.style.TextAppearance_Medium), 0,
756                briefText.length(), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
757        getPreferenceScreen().removeAll();
758    }
759
760    private void addMessagePreference(int messageId) {
761        if (mEmptyView != null) mEmptyView.setText(messageId);
762        getPreferenceScreen().removeAll();
763    }
764
765    protected void setProgressBarVisible(boolean visible) {
766        if (mProgressHeader != null) {
767            mProgressHeader.setVisibility(visible ? View.VISIBLE : View.GONE);
768        }
769    }
770
771    @Override
772    public void onWifiStateChanged(int state) {
773        Activity activity = getActivity();
774        if (activity != null) {
775            activity.invalidateOptionsMenu();
776        }
777
778        switch (state) {
779            case WifiManager.WIFI_STATE_ENABLING:
780                addMessagePreference(R.string.wifi_starting);
781                setProgressBarVisible(true);
782                break;
783
784            case WifiManager.WIFI_STATE_DISABLED:
785                setOffMessage();
786                setProgressBarVisible(false);
787                break;
788        }
789    }
790
791    @Override
792    public void onConnectedChanged() {
793        changeNextButtonState(mWifiTracker.isConnected());
794    }
795
796    /**
797     * Renames/replaces "Next" button when appropriate. "Next" button usually exists in
798     * Wifi setup screens, not in usual wifi settings screen.
799     *
800     * @param enabled true when the device is connected to a wifi network.
801     */
802    private void changeNextButtonState(boolean enabled) {
803        if (mEnableNextOnConnection && hasNextButton()) {
804            getNextButton().setEnabled(enabled);
805        }
806    }
807
808    @Override
809    public void onClick(DialogInterface dialogInterface, int button) {
810        if (button == WifiDialog.BUTTON_FORGET && mSelectedAccessPoint != null) {
811            forget();
812        } else if (button == WifiDialog.BUTTON_SUBMIT) {
813            if (mDialog != null) {
814                submit(mDialog.getController());
815            }
816        }
817    }
818
819    /* package */ void submit(WifiConfigController configController) {
820
821        final WifiConfiguration config = configController.getConfig();
822
823        if (config == null) {
824            if (mSelectedAccessPoint != null
825                    && mSelectedAccessPoint.isSaved()) {
826                connect(mSelectedAccessPoint.getConfig());
827            }
828        } else if (config.networkId != INVALID_NETWORK_ID) {
829            if (mSelectedAccessPoint != null) {
830                mWifiManager.save(config, mSaveListener);
831            }
832        } else {
833            if (configController.isEdit()) {
834                mWifiManager.save(config, mSaveListener);
835            } else {
836                connect(config);
837            }
838        }
839
840        mWifiTracker.resumeScanning();
841    }
842
843    /* package */ void forget() {
844        MetricsLogger.action(getActivity(), MetricsLogger.ACTION_WIFI_FORGET);
845        if (!mSelectedAccessPoint.isSaved()) {
846            if (mSelectedAccessPoint.getNetworkInfo() != null &&
847                    mSelectedAccessPoint.getNetworkInfo().getState() != State.DISCONNECTED) {
848                // Network is active but has no network ID - must be ephemeral.
849                mWifiManager.disableEphemeralNetwork(
850                        AccessPoint.convertToQuotedString(mSelectedAccessPoint.getSsidStr()));
851            } else {
852                // Should not happen, but a monkey seems to trigger it
853                Log.e(TAG, "Failed to forget invalid network " + mSelectedAccessPoint.getConfig());
854                return;
855            }
856        } else {
857            mWifiManager.forget(mSelectedAccessPoint.getConfig().networkId, mForgetListener);
858        }
859
860        mWifiTracker.resumeScanning();
861
862        // We need to rename/replace "Next" button in wifi setup context.
863        changeNextButtonState(false);
864    }
865
866    protected void connect(final WifiConfiguration config) {
867        MetricsLogger.action(getActivity(), MetricsLogger.ACTION_WIFI_CONNECT);
868        mWifiManager.connect(config, mConnectListener);
869    }
870
871    protected void connect(final int networkId) {
872        MetricsLogger.action(getActivity(), MetricsLogger.ACTION_WIFI_CONNECT);
873        mWifiManager.connect(networkId, mConnectListener);
874    }
875
876    /**
877     * Refreshes acccess points and ask Wifi module to scan networks again.
878     */
879    /* package */ void refreshAccessPoints() {
880        mWifiTracker.resumeScanning();
881
882        getPreferenceScreen().removeAll();
883    }
884
885    /**
886     * Called when "add network" button is pressed.
887     */
888    /* package */ void onAddNetworkPressed() {
889        MetricsLogger.action(getActivity(), MetricsLogger.ACTION_WIFI_ADD_NETWORK);
890        // No exact access point is selected.
891        mSelectedAccessPoint = null;
892        showDialog(null, true);
893    }
894
895    /* package */ int getAccessPointsCount() {
896        final boolean wifiIsEnabled = mWifiTracker.isWifiEnabled();
897        if (wifiIsEnabled) {
898            return getPreferenceScreen().getPreferenceCount();
899        } else {
900            return 0;
901        }
902    }
903
904    /**
905     * Requests wifi module to pause wifi scan. May be ignored when the module is disabled.
906     */
907    /* package */ void pauseWifiScan() {
908        mWifiTracker.pauseScanning();
909    }
910
911    /**
912     * Requests wifi module to resume wifi scan. May be ignored when the module is disabled.
913     */
914    /* package */ void resumeWifiScan() {
915        mWifiTracker.resumeScanning();
916    }
917
918    @Override
919    protected int getHelpResource() {
920        return R.string.help_url_wifi;
921    }
922
923    @Override
924    public void onAccessPointChanged(AccessPoint accessPoint) {
925        ((AccessPointPreference) accessPoint.getTag()).refresh();
926    }
927
928    @Override
929    public void onLevelChanged(AccessPoint accessPoint) {
930        ((AccessPointPreference) accessPoint.getTag()).onLevelChanged();
931    }
932
933    public static final SearchIndexProvider SEARCH_INDEX_DATA_PROVIDER =
934        new BaseSearchIndexProvider() {
935            @Override
936            public List<SearchIndexableRaw> getRawDataToIndex(Context context, boolean enabled) {
937                final List<SearchIndexableRaw> result = new ArrayList<SearchIndexableRaw>();
938                final Resources res = context.getResources();
939
940                // Add fragment title
941                SearchIndexableRaw data = new SearchIndexableRaw(context);
942                data.title = res.getString(R.string.wifi_settings);
943                data.screenTitle = res.getString(R.string.wifi_settings);
944                data.keywords = res.getString(R.string.keywords_wifi);
945                result.add(data);
946
947                // Add saved Wi-Fi access points
948                final Collection<AccessPoint> accessPoints =
949                        WifiTracker.getCurrentAccessPoints(context, true, false, false);
950                for (AccessPoint accessPoint : accessPoints) {
951                    data = new SearchIndexableRaw(context);
952                    data.title = accessPoint.getSsidStr();
953                    data.screenTitle = res.getString(R.string.wifi_settings);
954                    data.enabled = enabled;
955                    result.add(data);
956                }
957
958                return result;
959            }
960        };
961
962    /**
963     * Returns true if the config is not editable through Settings.
964     * @param context Context of caller
965     * @param config The WiFi config.
966     * @return true if the config is not editable through Settings.
967     */
968    static boolean isEditabilityLockedDown(Context context, WifiConfiguration config) {
969        return !canModifyNetwork(context, config);
970    }
971
972    /**
973     * This method is a stripped version of WifiConfigStore.canModifyNetwork.
974     * TODO: refactor to have only one method.
975     * @param context Context of caller
976     * @param config The WiFi config.
977     * @return true if Settings can modify the config.
978     */
979    static boolean canModifyNetwork(Context context, WifiConfiguration config) {
980        if (config == null) {
981            return true;
982        }
983
984        final DevicePolicyManager dpm = (DevicePolicyManager) context.getSystemService(
985                Context.DEVICE_POLICY_SERVICE);
986
987        // Check if device has DPM capability. If it has and dpm is still null, then we
988        // treat this case with suspicion and bail out.
989        final PackageManager pm = context.getPackageManager();
990        if (pm.hasSystemFeature(PackageManager.FEATURE_DEVICE_ADMIN) && dpm == null) {
991            return false;
992        }
993
994        boolean isConfigEligibleForLockdown = false;
995        if (dpm != null) {
996            final String deviceOwnerPackageName = dpm.getDeviceOwner();
997            if (deviceOwnerPackageName != null) {
998                try {
999                    final int deviceOwnerUid = pm.getPackageUid(deviceOwnerPackageName,
1000                            UserHandle.USER_OWNER);
1001                    isConfigEligibleForLockdown = deviceOwnerUid == config.creatorUid;
1002                } catch (NameNotFoundException e) {
1003                    // don't care
1004                }
1005            }
1006        }
1007        if (!isConfigEligibleForLockdown) {
1008            return true;
1009        }
1010
1011        final ContentResolver resolver = context.getContentResolver();
1012        final boolean isLockdownFeatureEnabled = Settings.Global.getInt(resolver,
1013                Settings.Global.WIFI_DEVICE_OWNER_CONFIGS_LOCKDOWN, 0) != 0;
1014        return !isLockdownFeatureEnabled;
1015    }
1016
1017}
1018