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