summaryrefslogtreecommitdiff
path: root/src/com/android/networkrecommendation/wakeup/WifiWakeupController.java
diff options
context:
space:
mode:
Diffstat (limited to 'src/com/android/networkrecommendation/wakeup/WifiWakeupController.java')
-rw-r--r--src/com/android/networkrecommendation/wakeup/WifiWakeupController.java282
1 files changed, 282 insertions, 0 deletions
diff --git a/src/com/android/networkrecommendation/wakeup/WifiWakeupController.java b/src/com/android/networkrecommendation/wakeup/WifiWakeupController.java
new file mode 100644
index 0000000..a62bfbd
--- /dev/null
+++ b/src/com/android/networkrecommendation/wakeup/WifiWakeupController.java
@@ -0,0 +1,282 @@
+/*
+ * Copyright (C) 2017 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.networkrecommendation.wakeup;
+
+import android.content.BroadcastReceiver;
+import android.content.ContentResolver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.database.ContentObserver;
+import android.net.wifi.ScanResult;
+import android.net.wifi.WifiConfiguration;
+import android.net.wifi.WifiManager;
+import android.os.Handler;
+import android.os.Looper;
+import android.provider.Settings;
+import android.support.annotation.VisibleForTesting;
+import android.text.TextUtils;
+import android.util.ArrayMap;
+import android.util.ArraySet;
+import android.util.Log;
+
+import com.android.networkrecommendation.util.WifiConfigurationUtil;
+
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.atomic.AtomicBoolean;
+
+/**
+ * Handles enabling Wi-Fi for the Wi-Fi Wakeup feature.
+ *
+ * <p>
+ * This class enables Wi-Fi when the user is near a network that would would autojoined if Wi-Fi
+ * were enabled. When a user disables Wi-Fi, Wi-Fi Wakeup will not enable Wi-Fi until the
+ * user's context has changed. For saved networks, this context change is defined by the user
+ * leaving the range of the saved SSIDs that were in range when the user disabled Wi-Fi.
+ *
+ * @hide
+ */
+public class WifiWakeupController {
+ private static final String TAG = "WifiWakeupController";
+ private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
+ private static final boolean VERBOSE = Log.isLoggable(TAG, Log.VERBOSE);
+
+ /** Number of scans to ensure that a previously in range AP is now out of range. */
+ private static final int NUM_SCANS_TO_CONFIRM_AP_LOSS = 3;
+
+ private final Context mContext;
+ private final ContentResolver mContentResolver;
+ private final WifiManager mWifiManager;
+ private final WifiWakeupNetworkSelector mWifiWakeupNetworkSelector;
+ private final Handler mHandler;
+ private final WifiWakeupNotificationHelper mWifiWakeupNotificationHelper;
+ private final AtomicBoolean mStarted;
+ @VisibleForTesting final ContentObserver mContentObserver;
+
+ private final Map<String, WifiConfiguration> mSavedNetworks = new ArrayMap<>();
+ private final Set<String> mSavedSsidsInLastScan = new ArraySet<>();
+ private final Set<String> mSavedSsids = new ArraySet<>();
+ private final Map<String, Integer> mSavedSsidsOnDisable = new ArrayMap<>();
+ private int mWifiState;
+ private int mWifiApState;
+ private boolean mWifiWakeupEnabled;
+ private boolean mAirplaneModeEnabled;
+
+ public WifiWakeupController(Context context, ContentResolver contentResolver, Looper looper,
+ WifiManager wifiManager, WifiWakeupNetworkSelector wifiWakeupNetworkSelector,
+ WifiWakeupNotificationHelper wifiWakeupNotificationHelper) {
+ mContext = context;
+ mContentResolver = contentResolver;
+ mHandler = new Handler(looper);
+ mWifiWakeupNotificationHelper = wifiWakeupNotificationHelper;
+ mStarted = new AtomicBoolean(false);
+ mWifiManager = wifiManager;
+ mWifiWakeupNetworkSelector = wifiWakeupNetworkSelector;
+ mContentObserver = new ContentObserver(mHandler) {
+ @Override
+ public void onChange(boolean selfChange) {
+ mWifiWakeupEnabled = Settings.Global.getInt(mContentResolver,
+ Settings.Global.WIFI_WAKEUP_ENABLED, 0) == 1;
+ mAirplaneModeEnabled = Settings.Global.getInt(mContentResolver,
+ Settings.Global.AIRPLANE_MODE_ON, 0) == 1;
+ }
+ };
+ }
+
+ private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ if (!mWifiWakeupEnabled) {
+ return;
+ }
+
+ if (WifiManager.WIFI_AP_STATE_CHANGED_ACTION.equals(intent.getAction())) {
+ handleWifiApStateChanged();
+ } else if (WifiManager.WIFI_STATE_CHANGED_ACTION.equals(intent.getAction())) {
+ handleWifiStateChanged();
+ } else if (WifiManager.SCAN_RESULTS_AVAILABLE_ACTION.equals(intent.getAction())) {
+ handleScanResultsAvailable();
+ } else if (WifiManager.CONFIGURED_NETWORKS_CHANGED_ACTION.equals(intent.getAction())) {
+ handleConfiguredNetworksChanged();
+ }
+ }
+ };
+
+ /** Starts {@link WifiWakeupController}. */
+ public void start() {
+ if (!mStarted.compareAndSet(false, true)) {
+ return;
+ }
+ if (DEBUG) {
+ Log.d(TAG, "Starting WifiWakeupController.");
+ }
+ IntentFilter filter = new IntentFilter();
+ filter.addAction(WifiManager.WIFI_STATE_CHANGED_ACTION);
+ filter.addAction(WifiManager.SCAN_RESULTS_AVAILABLE_ACTION);
+ filter.addAction(WifiManager.CONFIGURED_NETWORKS_CHANGED_ACTION);
+ filter.addAction(WifiManager.WIFI_AP_STATE_CHANGED_ACTION);
+ // TODO(b/33695273): conditionally register this receiver based on wifi enabled setting
+ mContext.registerReceiver(mBroadcastReceiver, filter, null, mHandler);
+ mContentResolver.registerContentObserver(Settings.Global.getUriFor(
+ Settings.Global.WIFI_WAKEUP_ENABLED), true, mContentObserver);
+ mContentResolver.registerContentObserver(Settings.Global.getUriFor(
+ Settings.Global.AIRPLANE_MODE_ON), true, mContentObserver);
+ mContentObserver.onChange(true);
+ handleWifiStateChanged();
+ handleWifiApStateChanged();
+ handleConfiguredNetworksChanged();
+ handleScanResultsAvailable();
+ }
+
+ /** Stops {@link WifiWakeupController}. */
+ public void stop() {
+ if (!mStarted.compareAndSet(true, false)) {
+ return;
+ }
+ if (DEBUG) {
+ Log.d(TAG, "Stopping WifiWakeupController.");
+ }
+ mContext.unregisterReceiver(mBroadcastReceiver);
+ mContentResolver.unregisterContentObserver(mContentObserver);
+ }
+
+ private void handleWifiApStateChanged() {
+ mWifiApState = mWifiManager.getWifiApState();
+ if (VERBOSE) {
+ Log.v(TAG, "handleWifiApStateChanged: " + mWifiApState);
+ }
+ }
+
+ private void handleConfiguredNetworksChanged() {
+ List<WifiConfiguration> wifiConfigurations = mWifiManager.getConfiguredNetworks();
+ if (wifiConfigurations == null) {
+ return;
+ }
+ if (VERBOSE) {
+ Log.v(TAG, "handleConfiguredNetworksChanged: " + wifiConfigurations.size());
+ }
+
+ mSavedNetworks.clear();
+ mSavedSsids.clear();
+ for (int i = 0; i < wifiConfigurations.size(); i++) {
+ WifiConfiguration wifiConfiguration = wifiConfigurations.get(i);
+ if (wifiConfiguration.status != WifiConfiguration.Status.ENABLED
+ && wifiConfiguration.status != WifiConfiguration.Status.CURRENT) {
+ continue; // Ignore networks that are not connected or enabled.
+ }
+ if (wifiConfiguration.useExternalScores) {
+ continue; // Ignore externally scored networks.
+ }
+ if (wifiConfiguration.hasNoInternetAccess()
+ || wifiConfiguration.isNoInternetAccessExpected()) {
+ continue; // Ignore networks that will likely not have internet access.
+ }
+ String ssid = WifiConfigurationUtil.removeDoubleQuotes(wifiConfiguration);
+ if (TextUtils.isEmpty(ssid)) {
+ continue;
+ }
+ mSavedNetworks.put(ssid, wifiConfiguration);
+ mSavedSsids.add(ssid);
+ }
+ mSavedSsidsInLastScan.retainAll(mSavedSsids);
+ }
+
+ private void handleWifiStateChanged() {
+ mWifiState = mWifiManager.getWifiState();
+ if (VERBOSE) {
+ Log.v(TAG, "handleWifiStateChanged: " + mWifiState);
+ }
+ switch (mWifiState) {
+ case WifiManager.WIFI_STATE_ENABLED:
+ mSavedSsidsOnDisable.clear();
+ break;
+ case WifiManager.WIFI_STATE_DISABLED:
+ for (String ssid : mSavedSsidsInLastScan) {
+ mSavedSsidsOnDisable.put(ssid, NUM_SCANS_TO_CONFIRM_AP_LOSS);
+ }
+ break;
+ }
+ }
+
+ private void handleScanResultsAvailable() {
+ List<ScanResult> scanResults = mWifiManager.getScanResults();
+ if (scanResults == null) {
+ return;
+ }
+ if (VERBOSE) {
+ Log.v(TAG, "handleScanResultsAvailable: " + scanResults.size());
+ }
+
+ mSavedSsidsInLastScan.clear();
+ for (int i = 0; i < scanResults.size(); i++) {
+ String ssid = scanResults.get(i).SSID;
+ if (mSavedSsids.contains(ssid)) {
+ mSavedSsidsInLastScan.add(ssid);
+ }
+ }
+
+ if (mAirplaneModeEnabled
+ || mWifiState != WifiManager.WIFI_STATE_DISABLED
+ || mWifiApState != WifiManager.WIFI_AP_STATE_DISABLED) {
+ return;
+ }
+
+ // Update mSavedSsidsOnDisable to remove ssids that the user has moved away from.
+ for (Map.Entry<String, Integer> entry : mSavedSsidsOnDisable.entrySet()) {
+ if (mSavedSsidsInLastScan.contains(entry.getKey())) {
+ mSavedSsidsOnDisable.put(entry.getKey(), NUM_SCANS_TO_CONFIRM_AP_LOSS);
+ } else {
+ if (entry.getValue() > 1) {
+ mSavedSsidsOnDisable.put(entry.getKey(), entry.getValue() - 1);
+ } else {
+ mSavedSsidsOnDisable.remove(entry.getKey());
+ }
+ }
+ }
+
+ if (!mSavedSsidsOnDisable.isEmpty()) {
+ if (DEBUG) {
+ Log.d(TAG, "Latest scan result contains ssids from the disabled set: "
+ + mSavedSsidsOnDisable);
+ }
+ return;
+ }
+
+ WifiConfiguration selectedNetwork = mWifiWakeupNetworkSelector.selectNetwork(mSavedNetworks,
+ scanResults);
+ if (selectedNetwork != null) {
+ if (DEBUG) {
+ Log.d(TAG, "Enabling wifi for ssid: " + selectedNetwork.SSID);
+ }
+ mWifiManager.setWifiEnabled(true /* enabled */);
+ mWifiWakeupNotificationHelper.maybeShowWifiEnabledNotification(selectedNetwork);
+ }
+ }
+
+ /** Dump debugging information. */
+ public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+ pw.println("mStarted " + mStarted.get());
+ pw.println("mWifiWakeupEnabled: " + mWifiWakeupEnabled);
+ pw.println("mSavedSsids: " + mSavedSsids);
+ pw.println("mSavedSsidsInLastScan: " + mSavedSsidsInLastScan);
+ pw.println("mSavedSsidsOnDisable: " + mSavedSsidsOnDisable);
+ }
+}