softAp framework changes

Remove AP persist settings
Add new netd interface
Handle errors
Handle AP config change
Bug: 2413908
Change-Id: I31a1221ef5479da8d4a2620f0f0ee0b62539bc69
diff --git a/services/java/com/android/server/NetworkManagementService.java b/services/java/com/android/server/NetworkManagementService.java
index aa9ced8..b114ca2 100644
--- a/services/java/com/android/server/NetworkManagementService.java
+++ b/services/java/com/android/server/NetworkManagementService.java
@@ -26,10 +26,13 @@
 import android.net.Uri;
 import android.net.InterfaceConfiguration;
 import android.net.INetworkManagementEventObserver;
+import android.net.wifi.WifiConfiguration;
+import android.net.wifi.WifiConfiguration.KeyMgmt;
 import android.os.INetworkManagementService;
 import android.os.Handler;
 import android.os.SystemProperties;
 import android.text.TextUtils;
+import android.util.Log;
 import android.util.Slog;
 import java.util.ArrayList;
 import java.util.StringTokenizer;
@@ -457,14 +460,38 @@
         throw new IllegalStateException("Got an empty response");
     }
 
-    public void startAccessPoint()
+    public void startAccessPoint(WifiConfiguration wifiConfig, String intf)
              throws IllegalStateException {
         mContext.enforceCallingOrSelfPermission(
                 android.Manifest.permission.CHANGE_NETWORK_STATE, "NetworkManagementService");
         mContext.enforceCallingOrSelfPermission(
             android.Manifest.permission.CHANGE_WIFI_STATE, "NetworkManagementService");
-        mConnector.doCommand(String.format("softap set"));
-        mConnector.doCommand(String.format("softap start"));
+        mConnector.doCommand(String.format("softap stop " + intf));
+        mConnector.doCommand(String.format("softap fwreload " + intf + " AP"));
+        mConnector.doCommand(String.format("softap start " + intf));
+        if (wifiConfig == null) {
+            mConnector.doCommand(String.format("softap set " + intf + " wl0.1"));
+        } else {
+            /**
+             * softap set arg1 arg2 arg3 [arg4 arg5 arg6 arg7 arg8]
+             * argv1 - wlan interface
+             * argv2 - softap interface
+             * argv3 - SSID
+             * argv4 - Security
+             * argv5 - Key
+             * argv6 - Channel
+             * argv7 - Preamble
+             * argv8 - Max SCB
+             *
+             * TODO: get a configurable softap interface from driver
+             */
+            String str = String.format("softap set " + intf + " wl0.1 %s %s %s", wifiConfig.SSID,
+                                       wifiConfig.allowedKeyManagement.get(KeyMgmt.WPA_PSK) ?
+                                       "wpa2-psk" : "open",
+                                       wifiConfig.preSharedKey);
+            mConnector.doCommand(str);
+        }
+        mConnector.doCommand(String.format("softap startap"));
     }
 
     public void stopAccessPoint() throws IllegalStateException {
@@ -472,7 +499,7 @@
                 android.Manifest.permission.CHANGE_NETWORK_STATE, "NetworkManagementService");
         mContext.enforceCallingOrSelfPermission(
             android.Manifest.permission.CHANGE_WIFI_STATE, "NetworkManagementService");
-        mConnector.doCommand("softap stop");
+        mConnector.doCommand("softap stopap");
     }
 
 }
diff --git a/services/java/com/android/server/WifiService.java b/services/java/com/android/server/WifiService.java
index 599eec5..4eb529c 100644
--- a/services/java/com/android/server/WifiService.java
+++ b/services/java/com/android/server/WifiService.java
@@ -46,6 +46,7 @@
 import android.net.wifi.ScanResult;
 import android.net.wifi.WifiConfiguration;
 import android.net.wifi.SupplicantState;
+import android.net.wifi.WifiConfiguration.KeyMgmt;
 import android.net.ConnectivityManager;
 import android.net.InterfaceConfiguration;
 import android.net.NetworkStateTracker;
@@ -81,6 +82,7 @@
 import com.android.internal.app.IBatteryStats;
 import android.app.backup.IBackupManager;
 import com.android.server.am.BatteryStatsService;
+import com.android.internal.R;
 
 /**
  * WifiService handles remote WiFi operation requests by implementing
@@ -105,6 +107,8 @@
     private boolean mDeviceIdle;
     private int mPluggedType;
 
+    private enum DriverAction {DRIVER_UNLOAD, NO_DRIVER_UNLOAD};
+
     // true if the user enabled Wifi while in airplane mode
     private boolean mAirplaneModeOverwridden;
 
@@ -271,11 +275,9 @@
      */
     public void startWifi() {
         boolean wifiEnabled = getPersistedWifiEnabled();
-        boolean wifiAPEnabled = wifiEnabled ? false : getPersistedWifiApEnabled();
         Slog.i(TAG, "WifiService starting up with Wi-Fi " +
                 (wifiEnabled ? "enabled" : "disabled"));
         setWifiEnabledBlocking(wifiEnabled, false, Process.myUid());
-        setWifiApEnabledBlocking(wifiAPEnabled, true, Process.myUid(), null);
     }
 
     private void updateTetherState(ArrayList<String> available, ArrayList<String> tethered) {
@@ -297,28 +299,22 @@
                     try {
                         ifcg = service.getInterfaceConfig(intf);
                         if (ifcg != null) {
-                            /* IP/netmask: 169.254.2.1/255.255.255.0 */
-                            ifcg.ipAddr = (169 << 24) + (254 << 16) + (2 << 8) + 1;
+                            /* IP/netmask: 169.254.2.2/255.255.255.0 */
+                            ifcg.ipAddr = (169 << 24) + (254 << 16) + (2 << 8) + 2;
                             ifcg.netmask = (255 << 24) + (255 << 16) + (255 << 8) + 0;
                             ifcg.interfaceFlags = "up";
 
                             service.setInterfaceConfig(intf, ifcg);
                         }
                     } catch (Exception e) {
-                        /**
-                         * TODO: Add broadcast to indicate tether failed
-                         */
                         Slog.e(TAG, "Error configuring interface " + intf + ", :" + e);
+                        setWifiApEnabledState(WIFI_AP_STATE_FAILED, 0, DriverAction.DRIVER_UNLOAD);
                         return;
                     }
 
-                    /**
-                     * TODO: Add broadcast to indicate tether failed
-                     */
-                    if(mCm.tether(intf) == ConnectivityManager.TETHER_ERROR_NO_ERROR) {
-                        Slog.d(TAG, "Tethered "+intf);
-                    } else {
+                    if(mCm.tether(intf) != ConnectivityManager.TETHER_ERROR_NO_ERROR) {
                         Slog.e(TAG, "Error tethering "+intf);
+                        setWifiApEnabledState(WIFI_AP_STATE_FAILED, 0, DriverAction.DRIVER_UNLOAD);
                     }
                     break;
                 }
@@ -434,7 +430,7 @@
         setWifiEnabledState(enable ? WIFI_STATE_ENABLING : WIFI_STATE_DISABLING, uid);
 
         if ((mWifiApState == WIFI_AP_STATE_ENABLED) && enable) {
-            setWifiApEnabledBlocking(false, true, Process.myUid(), null);
+            setWifiApEnabledBlocking(false, Process.myUid(), null);
         }
 
         if (enable) {
@@ -581,21 +577,6 @@
         return mWifiStateTracker.reassociate();
     }
 
-    private boolean getPersistedWifiApEnabled() {
-        final ContentResolver cr = mContext.getContentResolver();
-        try {
-            return Settings.Secure.getInt(cr, Settings.Secure.WIFI_AP_ON) == 1;
-        } catch (Settings.SettingNotFoundException e) {
-            Settings.Secure.putInt(cr, Settings.Secure.WIFI_AP_ON, 0);
-            return false;
-        }
-    }
-
-    private void persistWifiApEnabled(boolean enabled) {
-      final ContentResolver cr = mContext.getContentResolver();
-      Settings.Secure.putInt(cr, Settings.Secure.WIFI_AP_ON, enabled ? 1 : 0);
-    }
-
     /**
      * see {@link android.net.wifi.WifiManager#startAccessPoint(WifiConfiguration)}
      * @param wifiConfig SSID, security and channel details as
@@ -621,46 +602,101 @@
         return true;
     }
 
+    public WifiConfiguration getWifiApConfiguration() {
+        final ContentResolver cr = mContext.getContentResolver();
+        WifiConfiguration wifiConfig = new WifiConfiguration();
+        int authType;
+        try {
+            wifiConfig.SSID = Settings.Secure.getString(cr, Settings.Secure.WIFI_AP_SSID);
+            if (wifiConfig.SSID == null)
+                return null;
+            authType = Settings.Secure.getInt(cr, Settings.Secure.WIFI_AP_SECURITY);
+            wifiConfig.allowedKeyManagement.set(authType);
+            wifiConfig.preSharedKey = Settings.Secure.getString(cr, Settings.Secure.WIFI_AP_PASSWD);
+            return wifiConfig;
+        } catch (Settings.SettingNotFoundException e) {
+            Slog.e(TAG,"AP settings not found, returning");
+            return null;
+        }
+    }
+
+    private void persistApConfiguration(WifiConfiguration wifiConfig) {
+        final ContentResolver cr = mContext.getContentResolver();
+        boolean isWpa;
+        if (wifiConfig == null)
+            return;
+        Settings.Secure.putString(cr, Settings.Secure.WIFI_AP_SSID, wifiConfig.SSID);
+        isWpa = wifiConfig.allowedKeyManagement.get(KeyMgmt.WPA_PSK);
+        Settings.Secure.putInt(cr,
+                               Settings.Secure.WIFI_AP_SECURITY,
+                               isWpa ? KeyMgmt.WPA_PSK : KeyMgmt.NONE);
+        if (isWpa)
+            Settings.Secure.putString(cr, Settings.Secure.WIFI_AP_PASSWD, wifiConfig.preSharedKey);
+    }
+
     /**
      * Enables/disables Wi-Fi AP synchronously. The driver is loaded
      * and soft access point configured as a single operation.
      * @param enable {@code true} to turn Wi-Fi on, {@code false} to turn it off.
-     * @param persist {@code true} if the setting should be persisted.
      * @param uid The UID of the process making the request.
-     * @param config The WifiConfiguration for AP
+     * @param wifiConfig The WifiConfiguration for AP
      * @return {@code true} if the operation succeeds (or if the existing state
      *         is the same as the requested state)
      */
-    /**
-     * TODO: persist needs to go away in WifiService
-     * This will affect all persist related functions
-     * for Access Point
-     */
     private boolean setWifiApEnabledBlocking(boolean enable,
-                        boolean persist, int uid, WifiConfiguration wifiConfig) {
+                                int uid, WifiConfiguration wifiConfig) {
         final int eventualWifiApState = enable ? WIFI_AP_STATE_ENABLED : WIFI_AP_STATE_DISABLED;
 
         if (mWifiApState == eventualWifiApState) {
-            return true;
+            /* Configuration changed on a running access point */
+            if(enable && (wifiConfig != null)) {
+                try {
+                    persistApConfiguration(wifiConfig);
+                    nwService.stopAccessPoint();
+                    nwService.startAccessPoint(wifiConfig, mWifiStateTracker.getInterfaceName());
+                    return true;
+                } catch(Exception e) {
+                    Slog.e(TAG, "Exception in nwService during AP restart");
+                    setWifiApEnabledState(WIFI_AP_STATE_FAILED, uid, DriverAction.DRIVER_UNLOAD);
+                    return false;
+                }
+            } else {
+                return true;
+            }
         }
 
-        setWifiApEnabledState(enable ? WIFI_AP_STATE_ENABLING : WIFI_AP_STATE_DISABLING, uid);
-
-        if (enable && (mWifiStateTracker.getWifiState() == WIFI_STATE_ENABLED)) {
-            setWifiEnabledBlocking(false, true, Process.myUid());
-        }
+        setWifiApEnabledState(enable ? WIFI_AP_STATE_ENABLING :
+                                       WIFI_AP_STATE_DISABLING, uid, DriverAction.NO_DRIVER_UNLOAD);
 
         if (enable) {
+
+            /**
+             * Disable client mode for starting AP
+             */
+            if (mWifiStateTracker.getWifiState() == WIFI_STATE_ENABLED) {
+                setWifiEnabledBlocking(false, true, Process.myUid());
+            }
+
+            /* Use default config if there is no existing config */
+            if (wifiConfig == null && ((wifiConfig = getWifiApConfiguration()) == null)) {
+                wifiConfig = new WifiConfiguration();
+                wifiConfig.SSID = mContext.getString(R.string.wifi_tether_configure_ssid_default);
+                wifiConfig.allowedKeyManagement.set(KeyMgmt.NONE);
+            }
+            persistApConfiguration(wifiConfig);
+
             if (!mWifiStateTracker.loadDriver()) {
                 Slog.e(TAG, "Failed to load Wi-Fi driver for AP mode");
-                setWifiApEnabledState(WIFI_AP_STATE_FAILED, uid);
+                setWifiApEnabledState(WIFI_AP_STATE_FAILED, uid, DriverAction.NO_DRIVER_UNLOAD);
                 return false;
             }
 
             try {
-                nwService.startAccessPoint();
+                nwService.startAccessPoint(wifiConfig, mWifiStateTracker.getInterfaceName());
             } catch(Exception e) {
                 Slog.e(TAG, "Exception in startAccessPoint()");
+                setWifiApEnabledState(WIFI_AP_STATE_FAILED, uid, DriverAction.DRIVER_UNLOAD);
+                return false;
             }
 
         } else {
@@ -669,20 +705,18 @@
                 nwService.stopAccessPoint();
             } catch(Exception e) {
                 Slog.e(TAG, "Exception in stopAccessPoint()");
+                setWifiApEnabledState(WIFI_AP_STATE_FAILED, uid, DriverAction.DRIVER_UNLOAD);
+                return false;
             }
 
             if (!mWifiStateTracker.unloadDriver()) {
                 Slog.e(TAG, "Failed to unload Wi-Fi driver for AP mode");
-                setWifiApEnabledState(WIFI_AP_STATE_FAILED, uid);
+                setWifiApEnabledState(WIFI_AP_STATE_FAILED, uid, DriverAction.NO_DRIVER_UNLOAD);
                 return false;
             }
         }
 
-        // Success!
-        if (persist) {
-            persistWifiApEnabled(enable);
-        }
-        setWifiApEnabledState(eventualWifiApState, uid);
+        setWifiApEnabledState(eventualWifiApState, uid, DriverAction.NO_DRIVER_UNLOAD);
         return true;
     }
 
@@ -699,9 +733,16 @@
         return mWifiApState;
     }
 
-    private void setWifiApEnabledState(int wifiAPState, int uid) {
+    private void setWifiApEnabledState(int wifiAPState, int uid, DriverAction flag) {
         final int previousWifiApState = mWifiApState;
 
+        /**
+         * Unload the driver if going to a failed state
+         */
+        if ((mWifiApState == WIFI_AP_STATE_FAILED) && (flag == DriverAction.DRIVER_UNLOAD)) {
+            mWifiStateTracker.unloadDriver();
+        }
+
         long ident = Binder.clearCallingIdentity();
         try {
             if (wifiAPState == WIFI_AP_STATE_ENABLED) {
@@ -1694,7 +1735,7 @@
     private void sendAccessPointMessage(boolean enable, WifiConfiguration wifiConfig, int uid) {
         Message.obtain(mWifiHandler,
                 (enable ? MESSAGE_START_ACCESS_POINT : MESSAGE_STOP_ACCESS_POINT),
-                0, uid, wifiConfig).sendToTarget();
+                uid, 0, wifiConfig).sendToTarget();
     }
 
     private void updateWifiState() {
@@ -1841,15 +1882,13 @@
 
                 case MESSAGE_START_ACCESS_POINT:
                     setWifiApEnabledBlocking(true,
-                                             msg.arg1 == 1,
-                                             msg.arg2,
+                                             msg.arg1,
                                              (WifiConfiguration) msg.obj);
                     break;
 
                 case MESSAGE_STOP_ACCESS_POINT:
                     setWifiApEnabledBlocking(false,
-                                             msg.arg1 == 1,
-                                             msg.arg2,
+                                             msg.arg1,
                                              (WifiConfiguration) msg.obj);
                     sWakeLock.release();
                     break;