SysUI: Refactor the NetworkControllerImpl

Minimal changes to interface/callbacks, all of the changes are
internal and prepare for MSIM.

Separate out AccessPoint and MobileData from the NetworkController
interface to give some space.

A SignalController class has been created as a base for both
WifiSignalController and MobileSignalController, both of which
internally handle the state of their respective connectivity and
only reach up into the NetworkControllerImpl when completely
necessary (such as for combined carrier label).

Bug: 18222975
Change-Id: I75b954bbece187371cdb8571dd8420e7d2cad978
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/AccessPointControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/AccessPointControllerImpl.java
new file mode 100644
index 0000000..6fec97e
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/AccessPointControllerImpl.java
@@ -0,0 +1,252 @@
+/*
+ * Copyright (C) 2014 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.systemui.statusbar.policy;
+
+import android.app.ActivityManager;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.net.wifi.ScanResult;
+import android.net.wifi.WifiConfiguration;
+import android.net.wifi.WifiConfiguration.KeyMgmt;
+import android.net.wifi.WifiInfo;
+import android.net.wifi.WifiManager;
+import android.net.wifi.WifiManager.ActionListener;
+import android.os.UserHandle;
+import android.os.UserManager;
+import android.provider.Settings;
+import android.text.TextUtils;
+import android.util.ArrayMap;
+import android.util.ArraySet;
+import android.util.Log;
+
+import com.android.systemui.R;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.List;
+
+public class AccessPointControllerImpl implements NetworkController.AccessPointController {
+    private static final String TAG = "AccessPointController";
+    private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
+
+    // This string extra specifies a network to open the connect dialog on, so the user can enter
+    // network credentials.  This is used by quick settings for secured networks.
+    private static final String EXTRA_START_CONNECT_SSID = "wifi_start_connect_ssid";
+
+    private static final int[] ICONS = {
+        R.drawable.ic_qs_wifi_0,
+        R.drawable.ic_qs_wifi_full_1,
+        R.drawable.ic_qs_wifi_full_2,
+        R.drawable.ic_qs_wifi_full_3,
+        R.drawable.ic_qs_wifi_full_4,
+    };
+
+    private final Context mContext;
+    private final ArrayList<AccessPointCallback> mCallbacks = new ArrayList<AccessPointCallback>();
+    private final WifiManager mWifiManager;
+    private final UserManager mUserManager;
+    private final Receiver mReceiver = new Receiver();
+
+    private boolean mScanning;
+    private int mCurrentUser;
+
+    public AccessPointControllerImpl(Context context) {
+        mContext = context;
+        mWifiManager = (WifiManager) mContext.getSystemService(Context.WIFI_SERVICE);
+        mUserManager = (UserManager) mContext.getSystemService(Context.USER_SERVICE);
+        mCurrentUser = ActivityManager.getCurrentUser();
+    }
+
+    public boolean canConfigWifi() {
+        return !mUserManager.hasUserRestriction(UserManager.DISALLOW_CONFIG_WIFI,
+                new UserHandle(mCurrentUser));
+    }
+
+    public void onUserSwitched(int newUserId) {
+        mCurrentUser = newUserId;
+    }
+
+    @Override
+    public void addAccessPointCallback(AccessPointCallback callback) {
+        if (callback == null || mCallbacks.contains(callback)) return;
+        if (DEBUG) Log.d(TAG, "addCallback " + callback);
+        mCallbacks.add(callback);
+        mReceiver.setListening(!mCallbacks.isEmpty());
+    }
+
+    @Override
+    public void removeAccessPointCallback(AccessPointCallback callback) {
+        if (callback == null) return;
+        if (DEBUG) Log.d(TAG, "removeCallback " + callback);
+        mCallbacks.remove(callback);
+        mReceiver.setListening(!mCallbacks.isEmpty());
+    }
+
+    @Override
+    public void scanForAccessPoints() {
+        if (mScanning) return;
+        if (DEBUG) Log.d(TAG, "scan!");
+        mScanning = mWifiManager.startScan();
+        // Grab current networks immediately while we wait for scan.
+        updateAccessPoints();
+    }
+
+    public boolean connect(AccessPoint ap) {
+        if (ap == null) return false;
+        if (DEBUG) Log.d(TAG, "connect networkId=" + ap.networkId);
+        if (ap.networkId < 0) {
+            // Unknown network, need to add it.
+            if (ap.hasSecurity) {
+                Intent intent = new Intent(Settings.ACTION_WIFI_SETTINGS);
+                intent.putExtra(EXTRA_START_CONNECT_SSID, ap.ssid);
+                intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+                mContext.startActivity(intent);
+                return true;
+            } else {
+                WifiConfiguration config = new WifiConfiguration();
+                config.SSID = "\"" + ap.ssid + "\"";
+                config.allowedKeyManagement.set(KeyMgmt.NONE);
+                mWifiManager.connect(config, mConnectListener);
+            }
+        } else {
+            mWifiManager.connect(ap.networkId, mConnectListener);
+        }
+        return false;
+    }
+
+    private void fireCallback(AccessPoint[] aps) {
+        for (AccessPointCallback callback : mCallbacks) {
+            callback.onAccessPointsChanged(aps);
+        }
+    }
+
+    private static String trimDoubleQuotes(String v) {
+        return v != null && v.length() >= 2 && v.charAt(0) == '\"'
+                && v.charAt(v.length() - 1) == '\"' ? v.substring(1, v.length() - 1) : v;
+    }
+
+    private int getConnectedNetworkId() {
+        final WifiInfo wifiInfo = mWifiManager.getConnectionInfo();
+        return wifiInfo != null ? wifiInfo.getNetworkId() : AccessPoint.NO_NETWORK;
+    }
+
+    private ArrayMap<String, WifiConfiguration> getConfiguredNetworksBySsid() {
+        final List<WifiConfiguration> configs = mWifiManager.getConfiguredNetworks();
+        if (configs == null || configs.size() == 0) return ArrayMap.EMPTY;
+        final ArrayMap<String, WifiConfiguration> rt = new ArrayMap<String, WifiConfiguration>();
+        for (WifiConfiguration config : configs) {
+            rt.put(trimDoubleQuotes(config.SSID), config);
+        }
+        return rt;
+    }
+
+    private void updateAccessPoints() {
+        final int connectedNetworkId = getConnectedNetworkId();
+        if (DEBUG) Log.d(TAG, "connectedNetworkId: " + connectedNetworkId);
+        final List<ScanResult> scanResults = mWifiManager.getScanResults();
+        final ArrayMap<String, WifiConfiguration> configured = getConfiguredNetworksBySsid();
+        if (DEBUG) Log.d(TAG, "scanResults: " + scanResults);
+        final List<AccessPoint> aps = new ArrayList<AccessPoint>(scanResults.size());
+        final ArraySet<String> ssids = new ArraySet<String>();
+        for (ScanResult scanResult : scanResults) {
+            if (scanResult == null) {
+                continue;
+            }
+            final String ssid = scanResult.SSID;
+            if (TextUtils.isEmpty(ssid) || ssids.contains(ssid)) continue;
+            ssids.add(ssid);
+            final WifiConfiguration config = configured.get(ssid);
+            final int level = WifiManager.calculateSignalLevel(scanResult.level, ICONS.length);
+            final AccessPoint ap = new AccessPoint();
+            ap.isConfigured = config != null;
+            ap.networkId = config != null ? config.networkId : AccessPoint.NO_NETWORK;
+            ap.ssid = ssid;
+            ap.iconId = ICONS[level];
+            ap.isConnected = ap.networkId != AccessPoint.NO_NETWORK
+                    && ap.networkId == connectedNetworkId;
+            ap.level = level;
+            // Based on Settings AccessPoint#getSecurity, keep up to date
+            // with better methods of determining no security or not.
+            ap.hasSecurity = scanResult.capabilities.contains("WEP")
+                    || scanResult.capabilities.contains("PSK")
+                    || scanResult.capabilities.contains("EAP");
+            aps.add(ap);
+        }
+        Collections.sort(aps, mByStrength);
+        fireCallback(aps.toArray(new AccessPoint[aps.size()]));
+    }
+
+    private final ActionListener mConnectListener = new ActionListener() {
+        @Override
+        public void onSuccess() {
+            if (DEBUG) Log.d(TAG, "connect success");
+        }
+
+        @Override
+        public void onFailure(int reason) {
+            if (DEBUG) Log.d(TAG, "connect failure reason=" + reason);
+        }
+    };
+
+    private final Comparator<AccessPoint> mByStrength = new Comparator<AccessPoint> () {
+        @Override
+        public int compare(AccessPoint lhs, AccessPoint rhs) {
+            return -Integer.compare(score(lhs), score(rhs));
+        }
+
+        private int score(AccessPoint ap) {
+            return ap.level + (ap.isConnected ? 20 : 0) + (ap.isConfigured ? 10 : 0);
+        }
+    };
+
+    private final class Receiver extends BroadcastReceiver {
+        private boolean mRegistered;
+
+        public void setListening(boolean listening) {
+            if (listening && !mRegistered) {
+                if (DEBUG) Log.d(TAG, "Registering receiver");
+                final IntentFilter filter = new IntentFilter();
+                filter.addAction(WifiManager.WIFI_STATE_CHANGED_ACTION);
+                filter.addAction(WifiManager.SCAN_RESULTS_AVAILABLE_ACTION);
+                filter.addAction(WifiManager.NETWORK_IDS_CHANGED_ACTION);
+                filter.addAction(WifiManager.SUPPLICANT_STATE_CHANGED_ACTION);
+                filter.addAction(WifiManager.CONFIGURED_NETWORKS_CHANGED_ACTION);
+                filter.addAction(WifiManager.LINK_CONFIGURATION_CHANGED_ACTION);
+                filter.addAction(WifiManager.NETWORK_STATE_CHANGED_ACTION);
+                filter.addAction(WifiManager.RSSI_CHANGED_ACTION);
+                mContext.registerReceiver(this, filter);
+                mRegistered = true;
+            } else if (!listening && mRegistered) {
+                if (DEBUG) Log.d(TAG, "Unregistering receiver");
+                mContext.unregisterReceiver(this);
+                mRegistered = false;
+            }
+        }
+
+        @Override
+        public void onReceive(Context context, Intent intent) {
+            if (DEBUG) Log.d(TAG, "onReceive " + intent.getAction());
+            if (WifiManager.SCAN_RESULTS_AVAILABLE_ACTION.equals(intent.getAction())) {
+                updateAccessPoints();
+                mScanning = false;
+            }
+        }
+    }
+}