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;
+    }
+}