/* * Copyright 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.managedprovisioning.task; import static com.android.internal.util.Preconditions.checkNotNull; import android.content.Context; import android.net.ConnectivityManager; import android.net.wifi.WifiConfiguration; import android.net.wifi.WifiInfo; import android.net.wifi.WifiManager; import android.os.Handler; import com.android.internal.annotations.VisibleForTesting; import com.android.managedprovisioning.analytics.MetricsWriterFactory; import com.android.managedprovisioning.analytics.ProvisioningAnalyticsTracker; import com.android.managedprovisioning.common.ManagedProvisioningSharedPreferences; import com.android.managedprovisioning.common.ProvisionLogger; import com.android.managedprovisioning.common.SettingsFacade; import com.android.managedprovisioning.common.Utils; import com.android.managedprovisioning.model.ProvisioningParams; import com.android.managedprovisioning.task.wifi.NetworkMonitor; import com.android.managedprovisioning.task.wifi.WifiConfigurationProvider; /** * Adds a wifi network to the system and waits for it to successfully connect. If the system does * not support wifi, the adding or connection times out {@link #error(int)} will be called. */ public class AddWifiNetworkTask extends AbstractProvisioningTask implements NetworkMonitor.NetworkConnectedCallback { private static final int RETRY_SLEEP_DURATION_BASE_MS = 500; private static final int RETRY_SLEEP_MULTIPLIER = 2; private static final int MAX_RETRIES = 6; private static final int RECONNECT_TIMEOUT_MS = 60000; @VisibleForTesting static final int ADD_NETWORK_FAIL = -1; private final WifiConfigurationProvider mWifiConfigurationProvider; private final WifiManager mWifiManager; private final NetworkMonitor mNetworkMonitor; private Handler mHandler; private boolean mTaskDone = false; private final Utils mUtils; private Runnable mTimeoutRunnable; private Injector mInjector; public AddWifiNetworkTask( Context context, ProvisioningParams provisioningParams, Callback callback) { this( new NetworkMonitor(context, /* waitForValidated */ false), new WifiConfigurationProvider(), context, provisioningParams, callback, new Utils(), new Injector(), new ProvisioningAnalyticsTracker( MetricsWriterFactory.getMetricsWriter(context, new SettingsFacade()), new ManagedProvisioningSharedPreferences(context))); } @VisibleForTesting AddWifiNetworkTask( NetworkMonitor networkMonitor, WifiConfigurationProvider wifiConfigurationProvider, Context context, ProvisioningParams provisioningParams, Callback callback, Utils utils, Injector injector, ProvisioningAnalyticsTracker provisioningAnalyticsTracker) { super(context, provisioningParams, callback, provisioningAnalyticsTracker); mNetworkMonitor = checkNotNull(networkMonitor); mWifiConfigurationProvider = checkNotNull(wifiConfigurationProvider); mWifiManager = (WifiManager) context.getSystemService(Context.WIFI_SERVICE); mUtils = checkNotNull(utils); mInjector = checkNotNull(injector); } @Override public void run(int userId) { if (mProvisioningParams.wifiInfo == null) { success(); return; } if (mWifiManager == null || !enableWifi()) { ProvisionLogger.loge("Failed to enable wifi"); error(0); return; } if (isConnectedToSpecifiedWifi()) { success(); return; } mTaskDone = false; mHandler = new Handler(); mNetworkMonitor.startListening(this); connectToProvidedNetwork(); } private void connectToProvidedNetwork() { WifiConfiguration wifiConf = mWifiConfigurationProvider.generateWifiConfiguration(mProvisioningParams.wifiInfo); if (wifiConf == null) { ProvisionLogger.loge("WifiConfiguration is null"); error(0); return; } int netId = tryAddingNetwork(wifiConf); if (netId == ADD_NETWORK_FAIL) { ProvisionLogger.loge("Unable to add network after trying " + MAX_RETRIES + " times."); error(0); return; } // Setting disableOthers to 'true' should trigger a connection attempt. mWifiManager.enableNetwork(netId, true); mWifiManager.saveConfiguration(); // Network was successfully saved, now connect to it. if (!mWifiManager.reconnect()) { ProvisionLogger.loge("Unable to connect to wifi"); error(0); return; } // NetworkMonitor will call onNetworkConnected when in Wifi mode. // Post time out event in case the NetworkMonitor doesn't call back. mTimeoutRunnable = () -> finishTask(false); mHandler.postDelayed(mTimeoutRunnable, RECONNECT_TIMEOUT_MS); } private int tryAddingNetwork(WifiConfiguration wifiConf) { int netId = mWifiManager.addNetwork(wifiConf); int retriesLeft = MAX_RETRIES; int durationNextSleep = RETRY_SLEEP_DURATION_BASE_MS; while(netId == -1 && retriesLeft > 0) { ProvisionLogger.loge("Retrying in " + durationNextSleep + " ms."); try { mInjector.threadSleep(durationNextSleep); } catch (InterruptedException e) { ProvisionLogger.loge("Retry interrupted."); } durationNextSleep *= RETRY_SLEEP_MULTIPLIER; retriesLeft--; netId = mWifiManager.addNetwork(wifiConf); } return netId; } private boolean enableWifi() { return mWifiManager.isWifiEnabled() || mWifiManager.setWifiEnabled(true); } @Override public void onNetworkConnected() { ProvisionLogger.logd("onNetworkConnected"); if (isConnectedToSpecifiedWifi()) { ProvisionLogger.logd("Connected to the correct network"); finishTask(true); // Remove time out callback. mHandler.removeCallbacks(mTimeoutRunnable); } } private synchronized void finishTask(boolean isSuccess) { if (mTaskDone) { return; } mTaskDone = true; mNetworkMonitor.stopListening(); if (isSuccess) { success(); } else { error(0); } } private boolean isConnectedToSpecifiedWifi() { if (!mUtils.isNetworkTypeConnected(mContext, ConnectivityManager.TYPE_WIFI)) { ProvisionLogger.logd("Not connected to WIFI"); return false; } WifiInfo connectionInfo = mWifiManager.getConnectionInfo(); if (connectionInfo == null) { ProvisionLogger.logd("connection info is null"); return false; } String connectedSSID = mWifiManager.getConnectionInfo().getSSID(); if (!mProvisioningParams.wifiInfo.ssid.equals(connectedSSID)) { ProvisionLogger.logd("Wanted to connect SSID " + mProvisioningParams.wifiInfo.ssid + ", but it is now connected to " + connectedSSID); return false; } return true; } @VisibleForTesting static class Injector { public void threadSleep(long milliseconds) throws InterruptedException { Thread.sleep(milliseconds); } } }