Add AP support for tethering
First phase of the AP support in
UI and framework
Bug: 2421638
Change-Id: I0b6c31be5fe79bd4d33c292464d271b054754e8d
diff --git a/src/com/android/settings/TetherSettings.java b/src/com/android/settings/TetherSettings.java
index 3e41bb8..9430c19 100644
--- a/src/com/android/settings/TetherSettings.java
+++ b/src/com/android/settings/TetherSettings.java
@@ -16,6 +16,8 @@
package com.android.settings;
+import com.android.settings.wifi.WifiApEnabler;
+
import android.os.Bundle;
import android.os.SystemProperties;
import android.content.BroadcastReceiver;
@@ -32,14 +34,21 @@
import android.util.Log;
import java.util.ArrayList;
+
/*
* Displays preferences for Tethering.
*/
public class TetherSettings extends PreferenceActivity {
private static final String USB_TETHER_SETTINGS = "usb_tether_settings";
+ private static final String ENABLE_WIFI_AP = "enable_wifi_ap";
+ private static final String WIFI_AP_SETTINGS = "wifi_ap_settings";
private PreferenceScreen mUsbTether;
+ private CheckBoxPreference mEnableWifiAp;
+ private PreferenceScreen mWifiApSettings;
+ private WifiApEnabler mWifiApEnabler;
+
private BroadcastReceiver mTetherChangeReceiver;
private String[] mUsbRegexs;
@@ -55,6 +64,8 @@
addPreferencesFromResource(R.xml.tether_prefs);
mUsbTether = (PreferenceScreen) findPreference(USB_TETHER_SETTINGS);
+ mEnableWifiAp = (CheckBoxPreference) findPreference(ENABLE_WIFI_AP);
+ mWifiApSettings = (PreferenceScreen) findPreference(WIFI_AP_SETTINGS);
ConnectivityManager cm =
(ConnectivityManager)getSystemService(Context.CONNECTIVITY_SERVICE);
@@ -62,7 +73,13 @@
if (mUsbRegexs.length == 0) {
getPreferenceScreen().removePreference(mUsbTether);
}
+
mWifiRegexs = cm.getTetherableWifiRegexs();
+ if (mWifiRegexs.length == 0) {
+ getPreferenceScreen().removePreference(mEnableWifiAp);
+ getPreferenceScreen().removePreference(mWifiApSettings);
+ }
+ mWifiApEnabler = new WifiApEnabler(this, mEnableWifiAp);
}
@@ -91,6 +108,7 @@
Intent intent = registerReceiver(mTetherChangeReceiver, filter);
if (intent != null) mTetherChangeReceiver.onReceive(this, intent);
+ mWifiApEnabler.resume();
}
@Override
@@ -98,6 +116,7 @@
super.onPause();
unregisterReceiver(mTetherChangeReceiver);
mTetherChangeReceiver = null;
+ mWifiApEnabler.pause();
}
private void updateState(ArrayList<String> available, ArrayList<String> tethered,
diff --git a/src/com/android/settings/wifi/AccessPoint.java b/src/com/android/settings/wifi/AccessPoint.java
index 9e40bd0..3ccab21 100644
--- a/src/com/android/settings/wifi/AccessPoint.java
+++ b/src/com/android/settings/wifi/AccessPoint.java
@@ -49,7 +49,7 @@
private DetailedState mState;
private ImageView mSignal;
- private static int getSecurity(WifiConfiguration config) {
+ static int getSecurity(WifiConfiguration config) {
if (config.allowedKeyManagement.get(KeyMgmt.WPA_PSK)) {
return SECURITY_PSK;
}
diff --git a/src/com/android/settings/wifi/WifiApDialog.java b/src/com/android/settings/wifi/WifiApDialog.java
new file mode 100644
index 0000000..79372d0
--- /dev/null
+++ b/src/com/android/settings/wifi/WifiApDialog.java
@@ -0,0 +1,198 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settings.wifi;
+
+import com.android.settings.R;
+
+import android.app.AlertDialog;
+import android.content.Context;
+import android.content.DialogInterface;
+import android.net.wifi.WifiConfiguration;
+import android.net.wifi.WifiConfiguration.AuthAlgorithm;
+import android.net.wifi.WifiConfiguration.KeyMgmt;
+import android.os.Bundle;
+import android.text.Editable;
+import android.text.InputType;
+import android.text.TextWatcher;
+import android.util.Log;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.AdapterView;
+import android.widget.ArrayAdapter;
+import android.widget.CheckBox;
+import android.widget.EditText;
+import android.widget.Spinner;
+import android.widget.TextView;
+
+/**
+ * Dialog to configure the SSID and security settings
+ * for Access Point operation
+ */
+class WifiApDialog extends AlertDialog implements View.OnClickListener,
+ TextWatcher, AdapterView.OnItemSelectedListener {
+
+ static final int BUTTON_SUBMIT = DialogInterface.BUTTON_POSITIVE;
+
+ private final DialogInterface.OnClickListener mListener;
+
+ private View mView;
+ private TextView mSsid;
+ private int mSecurityType = AccessPoint.SECURITY_NONE;
+ private EditText mPassword;
+
+ WifiConfiguration mWifiConfig;
+
+ public WifiApDialog(Context context, DialogInterface.OnClickListener listener,
+ WifiConfiguration wifiConfig) {
+ super(context);
+ mListener = listener;
+ mWifiConfig = wifiConfig;
+ if (wifiConfig != null)
+ mSecurityType = AccessPoint.getSecurity(wifiConfig);
+ }
+
+ public WifiConfiguration getConfig() {
+
+ WifiConfiguration config = new WifiConfiguration();
+
+ config.SSID = mSsid.getText().toString();
+
+ switch (mSecurityType) {
+ case AccessPoint.SECURITY_NONE:
+ config.allowedKeyManagement.set(KeyMgmt.NONE);
+ return config;
+
+ case AccessPoint.SECURITY_WEP:
+ config.allowedKeyManagement.set(KeyMgmt.NONE);
+ config.allowedAuthAlgorithms.set(AuthAlgorithm.SHARED);
+ if (mPassword.length() != 0) {
+ int length = mPassword.length();
+ String password = mPassword.getText().toString();
+ // WEP-40, WEP-104, and 256-bit WEP (WEP-232?)
+ if ((length == 10 || length == 26 || length == 58) &&
+ password.matches("[0-9A-Fa-f]*")) {
+ config.wepKeys[0] = password;
+ } else {
+ config.wepKeys[0] = '"' + password + '"';
+ }
+ }
+ return config;
+
+ case AccessPoint.SECURITY_PSK:
+ config.allowedKeyManagement.set(KeyMgmt.WPA_PSK);
+ config.allowedAuthAlgorithms.set(AuthAlgorithm.OPEN);
+ if (mPassword.length() != 0) {
+ String password = mPassword.getText().toString();
+ if (password.matches("[0-9A-Fa-f]{64}")) {
+ config.preSharedKey = password;
+ } else {
+ config.preSharedKey = '"' + password + '"';
+ }
+ }
+ return config;
+ }
+ return null;
+ }
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+
+ Spinner mSecurity = ((Spinner) mView.findViewById(R.id.security));
+ mView = getLayoutInflater().inflate(R.layout.wifi_ap_dialog, null);
+
+ setView(mView);
+ setInverseBackgroundForced(true);
+
+ Context context = getContext();
+
+ setTitle(R.string.wifi_ap_configure_network);
+ mView.findViewById(R.id.type).setVisibility(View.VISIBLE);
+ mSsid = (TextView) mView.findViewById(R.id.ssid);
+ mPassword = (EditText) mView.findViewById(R.id.password);
+
+ setButton(BUTTON_SUBMIT, context.getString(R.string.wifi_save), mListener);
+ setButton(DialogInterface.BUTTON_NEGATIVE,
+ context.getString(R.string.wifi_cancel), mListener);
+
+ if (mWifiConfig != null) {
+ mSsid.setText(mWifiConfig.SSID);
+ switch (mSecurityType) {
+ case AccessPoint.SECURITY_WEP:
+ mPassword.setText(mWifiConfig.wepKeys[0]);
+ break;
+ case AccessPoint.SECURITY_PSK:
+ mPassword.setText(mWifiConfig.preSharedKey);
+ break;
+ }
+ mSecurity.setSelection(mSecurityType);
+ }
+
+ mSsid.addTextChangedListener(this);
+ mPassword.addTextChangedListener(this);
+ ((CheckBox) mView.findViewById(R.id.show_password)).setOnClickListener(this);
+ mSecurity.setOnItemSelectedListener(this);
+
+ super.onCreate(savedInstanceState);
+
+ showSecurityFields();
+ validate();
+ }
+
+ private void validate() {
+ if ((mSsid != null && mSsid.length() == 0) ||
+ (mSecurityType == AccessPoint.SECURITY_WEP && mPassword.length() == 0) ||
+ (mSecurityType == AccessPoint.SECURITY_PSK && mPassword.length() < 8)) {
+ getButton(BUTTON_SUBMIT).setEnabled(false);
+ } else {
+ getButton(BUTTON_SUBMIT).setEnabled(true);
+ }
+ }
+
+ public void onClick(View view) {
+ mPassword.setInputType(
+ InputType.TYPE_CLASS_TEXT | (((CheckBox) view).isChecked() ?
+ InputType.TYPE_TEXT_VARIATION_VISIBLE_PASSWORD :
+ InputType.TYPE_TEXT_VARIATION_PASSWORD));
+ }
+
+ public void onTextChanged(CharSequence s, int start, int before, int count) {
+ }
+
+ public void beforeTextChanged(CharSequence s, int start, int count, int after) {
+ }
+
+ public void afterTextChanged(Editable editable) {
+ validate();
+ }
+
+ public void onItemSelected(AdapterView parent, View view, int position, long id) {
+ mSecurityType = position;
+ showSecurityFields();
+ validate();
+ }
+
+ public void onNothingSelected(AdapterView parent) {
+ }
+
+ private void showSecurityFields() {
+ if (mSecurityType == AccessPoint.SECURITY_NONE) {
+ mView.findViewById(R.id.fields).setVisibility(View.GONE);
+ return;
+ }
+ mView.findViewById(R.id.fields).setVisibility(View.VISIBLE);
+ }
+}
diff --git a/src/com/android/settings/wifi/WifiApEnabler.java b/src/com/android/settings/wifi/WifiApEnabler.java
new file mode 100644
index 0000000..7e05173
--- /dev/null
+++ b/src/com/android/settings/wifi/WifiApEnabler.java
@@ -0,0 +1,146 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settings.wifi;
+
+import com.android.settings.R;
+import com.android.settings.WirelessSettings;
+
+import android.app.AlertDialog;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.DialogInterface;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.net.NetworkInfo;
+import android.net.wifi.SupplicantState;
+import android.net.wifi.WifiInfo;
+import android.net.wifi.WifiManager;
+import android.preference.Preference;
+import android.preference.CheckBoxPreference;
+import android.provider.Settings;
+import android.text.TextUtils;
+import android.util.Log;
+import android.widget.Toast;
+
+public class WifiApEnabler implements Preference.OnPreferenceChangeListener,
+ DialogInterface.OnClickListener {
+ private final Context mContext;
+ private final CheckBoxPreference mCheckBox;
+ private final CharSequence mOriginalSummary;
+
+ private final WifiManager mWifiManager;
+ private final IntentFilter mIntentFilter;
+ private AlertDialog mAlertDialog = null;
+
+ private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ String action = intent.getAction();
+ if (WifiManager.WIFI_AP_STATE_CHANGED_ACTION.equals(action)) {
+ handleWifiApStateChanged(intent.getIntExtra(
+ WifiManager.EXTRA_WIFI_AP_STATE, WifiManager.WIFI_AP_STATE_FAILED));
+ }
+ }
+ };
+
+ public WifiApEnabler(Context context, CheckBoxPreference checkBox) {
+ mContext = context;
+ mCheckBox = checkBox;
+ mOriginalSummary = checkBox.getSummary();
+ checkBox.setPersistent(false);
+
+ mWifiManager = (WifiManager) context.getSystemService(Context.WIFI_SERVICE);
+ mIntentFilter = new IntentFilter(WifiManager.WIFI_AP_STATE_CHANGED_ACTION);
+ }
+
+ public void resume() {
+ mContext.registerReceiver(mReceiver, mIntentFilter);
+ mCheckBox.setOnPreferenceChangeListener(this);
+ }
+
+ public void pause() {
+ mContext.unregisterReceiver(mReceiver);
+ mCheckBox.setOnPreferenceChangeListener(null);
+ if (mAlertDialog != null) {
+ mAlertDialog.dismiss();
+ mAlertDialog = null;
+ }
+ }
+
+ public boolean onPreferenceChange(Preference preference, Object value) {
+ boolean enable = (Boolean) value;
+
+ if (enable && mWifiManager.isWifiEnabled()) {
+ AlertDialog.Builder builder = new AlertDialog.Builder(mContext);
+ builder.setMessage(R.string.wifi_ap_tether_message)
+ .setCancelable(false)
+ .setPositiveButton(android.R.string.ok, this)
+ .setNegativeButton(android.R.string.cancel, this);
+
+ mAlertDialog = builder.create();
+ mAlertDialog.show();
+ } else {
+ setUpAccessPoint(enable);
+ }
+
+ return false;
+ }
+
+ public void onClick(DialogInterface dialog, int id) {
+ if(id == DialogInterface.BUTTON_POSITIVE ) {
+ setUpAccessPoint(true);
+ } else if (id == DialogInterface.BUTTON_NEGATIVE) {
+ dialog.dismiss();
+ mAlertDialog = null;
+ }
+ }
+
+ private void setUpAccessPoint(boolean enable) {
+ if (mWifiManager.setWifiApEnabled(null, enable)) {
+ mCheckBox.setEnabled(false);
+ } else {
+ mCheckBox.setSummary(R.string.wifi_error);
+ }
+ }
+
+ private void handleWifiApStateChanged(int state) {
+ switch (state) {
+ case WifiManager.WIFI_AP_STATE_ENABLING:
+ mCheckBox.setSummary(R.string.wifi_starting);
+ mCheckBox.setEnabled(false);
+ break;
+ case WifiManager.WIFI_AP_STATE_ENABLED:
+ mCheckBox.setChecked(true);
+ mCheckBox.setSummary(null);
+ mCheckBox.setEnabled(true);
+ break;
+ case WifiManager.WIFI_AP_STATE_DISABLING:
+ mCheckBox.setSummary(R.string.wifi_stopping);
+ mCheckBox.setEnabled(false);
+ break;
+ case WifiManager.WIFI_AP_STATE_DISABLED:
+ mCheckBox.setChecked(false);
+ mCheckBox.setSummary(mOriginalSummary);
+ mCheckBox.setEnabled(true);
+ break;
+ default:
+ mCheckBox.setChecked(false);
+ mCheckBox.setSummary(R.string.wifi_error);
+ mCheckBox.setEnabled(true);
+ }
+ }
+}
diff --git a/src/com/android/settings/wifi/WifiApSettings.java b/src/com/android/settings/wifi/WifiApSettings.java
new file mode 100644
index 0000000..17900fe
--- /dev/null
+++ b/src/com/android/settings/wifi/WifiApSettings.java
@@ -0,0 +1,151 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settings.wifi;
+
+import com.android.settings.R;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.DialogInterface;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.preference.ListPreference;
+import android.preference.Preference;
+import android.preference.PreferenceActivity;
+import android.preference.PreferenceScreen;
+import android.preference.CheckBoxPreference;
+import android.provider.Settings;
+import android.util.Log;
+import android.net.wifi.WifiConfiguration;
+import android.net.wifi.WifiManager;
+import android.os.Bundle;
+
+/*
+ * Displays preferences for Tethering.
+ */
+public class WifiApSettings extends PreferenceActivity
+ implements DialogInterface.OnClickListener, Preference.OnPreferenceChangeListener {
+
+ private static final String WIFI_AP_SSID_AND_SECURITY = "wifi_ap_ssid_and_security";
+ private static final String WIFI_AP_CHANNEL = "wifi_ap_channel";
+ private static final String ENABLE_WIFI_AP = "enable_wifi_ap";
+
+ private Preference mCreateNetwork;
+ private ListPreference mChannel;
+ private CheckBoxPreference mEnableWifiAp;
+
+ private WifiApDialog mDialog;
+ private WifiManager mWifiManager;
+ private WifiApEnabler mWifiApEnabler;
+ private WifiConfiguration mWifiConfig = null;
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ mWifiManager = (WifiManager) getSystemService(Context.WIFI_SERVICE);
+
+ addPreferencesFromResource(R.xml.wifi_ap_settings);
+
+ mCreateNetwork = findPreference(WIFI_AP_SSID_AND_SECURITY);
+ mChannel = (ListPreference) findPreference(WIFI_AP_CHANNEL);
+ mEnableWifiAp = (CheckBoxPreference) findPreference(ENABLE_WIFI_AP);
+
+ mWifiApEnabler = new WifiApEnabler(this, mEnableWifiAp);
+
+ initChannels();
+ }
+
+ public void initChannels() {
+ mChannel.setOnPreferenceChangeListener(this);
+
+ int numChannels = mWifiManager.getNumAllowedChannels();
+
+ String[] entries = new String[numChannels];
+ String[] entryValues = new String[numChannels];
+
+ for (int i = 1; i <= numChannels; i++) {
+ entries[i-1] = "Channel "+i;
+ entryValues[i-1] = i+"";
+ }
+
+ mChannel.setEntries(entries);
+ mChannel.setEntryValues(entryValues);
+ mChannel.setEnabled(true);
+ /**
+ * TODO: randomize initial channel chosen
+ */
+ mChannel.setValue("2");
+ }
+
+ @Override
+ protected void onResume() {
+ super.onResume();
+ mWifiApEnabler.resume();
+ }
+
+ @Override
+ protected void onPause() {
+ super.onPause();
+ mWifiApEnabler.pause();
+ }
+
+ @Override
+ public boolean onPreferenceTreeClick(PreferenceScreen screen, Preference preference) {
+ if (preference == mCreateNetwork) {
+ showDialog();
+ }
+ return true;
+ }
+
+ private void showDialog() {
+ if (mDialog != null) {
+ mDialog.dismiss();
+ }
+ mDialog = new WifiApDialog(this, this, mWifiConfig);
+ mDialog.show();
+ }
+
+ public void onClick(DialogInterface dialogInterface, int button) {
+ /**
+ * TODO: Needs work
+ */
+ mWifiConfig = mDialog.getConfig();
+
+ if(mWifiConfig.SSID != null)
+ mCreateNetwork.setSummary(mWifiConfig.SSID);
+
+ /**
+ * TODO: set SSID and security
+ */
+ }
+
+ public boolean onPreferenceChange(Preference preference, Object newValue) {
+ /**
+ * TODO: Needs work
+ */
+
+ String key = preference.getKey();
+ if (key == null) return true;
+
+ if (key.equals(WIFI_AP_CHANNEL)) {
+ int chosenChannel = Integer.parseInt((String) newValue);
+ if(newValue != null)
+ mChannel.setSummary(newValue.toString());
+ }
+ return true;
+ }
+}