1 /* 2 * Copyright 2014, The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.managedprovisioning.task; 18 19 import static com.android.internal.util.Preconditions.checkNotNull; 20 21 import android.content.Context; 22 import android.net.wifi.WifiConfiguration; 23 import android.net.wifi.WifiInfo; 24 import android.net.wifi.WifiManager; 25 import android.os.Handler; 26 27 import com.android.internal.annotations.VisibleForTesting; 28 import com.android.managedprovisioning.R; 29 import com.android.managedprovisioning.common.ProvisionLogger; 30 import com.android.managedprovisioning.common.Utils; 31 import com.android.managedprovisioning.model.ProvisioningParams; 32 import com.android.managedprovisioning.task.wifi.NetworkMonitor; 33 import com.android.managedprovisioning.task.wifi.WifiConfigurationProvider; 34 35 /** 36 * Adds a wifi network to the system and waits for it to successfully connect. If the system does 37 * not support wifi, the adding or connection times out {@link #error(int)} will be called. 38 */ 39 public class AddWifiNetworkTask extends AbstractProvisioningTask 40 implements NetworkMonitor.NetworkConnectedCallback { 41 private static final int RETRY_SLEEP_DURATION_BASE_MS = 500; 42 private static final int RETRY_SLEEP_MULTIPLIER = 2; 43 private static final int MAX_RETRIES = 6; 44 private static final int RECONNECT_TIMEOUT_MS = 60000; 45 @VisibleForTesting static final int ADD_NETWORK_FAIL = -1; 46 47 private final WifiConfigurationProvider mWifiConfigurationProvider; 48 private final WifiManager mWifiManager; 49 private final NetworkMonitor mNetworkMonitor; 50 51 private Handler mHandler; 52 private boolean mTaskDone = false; 53 54 private final Utils mUtils; 55 private Runnable mTimeoutRunnable; 56 private Injector mInjector; 57 AddWifiNetworkTask( Context context, ProvisioningParams provisioningParams, Callback callback)58 public AddWifiNetworkTask( 59 Context context, 60 ProvisioningParams provisioningParams, 61 Callback callback) { 62 this( 63 new NetworkMonitor(context), 64 new WifiConfigurationProvider(), 65 context, provisioningParams, callback, new Utils(), new Injector()); 66 } 67 68 @VisibleForTesting AddWifiNetworkTask( NetworkMonitor networkMonitor, WifiConfigurationProvider wifiConfigurationProvider, Context context, ProvisioningParams provisioningParams, Callback callback, Utils utils, Injector injector)69 AddWifiNetworkTask( 70 NetworkMonitor networkMonitor, 71 WifiConfigurationProvider wifiConfigurationProvider, 72 Context context, 73 ProvisioningParams provisioningParams, 74 Callback callback, 75 Utils utils, 76 Injector injector) { 77 super(context, provisioningParams, callback); 78 79 mNetworkMonitor = checkNotNull(networkMonitor); 80 mWifiConfigurationProvider = checkNotNull(wifiConfigurationProvider); 81 mWifiManager = (WifiManager) context.getSystemService(Context.WIFI_SERVICE); 82 mUtils = checkNotNull(utils); 83 mInjector = checkNotNull(injector); 84 } 85 86 @Override run(int userId)87 public void run(int userId) { 88 if (mProvisioningParams.wifiInfo == null) { 89 success(); 90 return; 91 } 92 93 if (mWifiManager == null || !enableWifi()) { 94 ProvisionLogger.loge("Failed to enable wifi"); 95 error(0); 96 return; 97 } 98 99 if (isConnectedToSpecifiedWifi()) { 100 success(); 101 return; 102 } 103 104 mTaskDone = false; 105 mHandler = new Handler(); 106 mNetworkMonitor.startListening(this); 107 connectToProvidedNetwork(); 108 } 109 110 @Override getStatusMsgId()111 public int getStatusMsgId() { 112 return R.string.progress_connect_to_wifi; 113 } 114 connectToProvidedNetwork()115 private void connectToProvidedNetwork() { 116 WifiConfiguration wifiConf = 117 mWifiConfigurationProvider.generateWifiConfiguration(mProvisioningParams.wifiInfo); 118 119 if (wifiConf == null) { 120 ProvisionLogger.loge("WifiConfiguration is null"); 121 error(0); 122 return; 123 } 124 125 int netId = tryAddingNetwork(wifiConf); 126 127 if (netId == ADD_NETWORK_FAIL) { 128 ProvisionLogger.loge("Unable to add network after trying " + MAX_RETRIES + " times."); 129 error(0); 130 return; 131 } 132 133 // Setting disableOthers to 'true' should trigger a connection attempt. 134 mWifiManager.enableNetwork(netId, true); 135 mWifiManager.saveConfiguration(); 136 137 // Network was successfully saved, now connect to it. 138 if (!mWifiManager.reconnect()) { 139 ProvisionLogger.loge("Unable to connect to wifi"); 140 error(0); 141 return; 142 } 143 144 // NetworkMonitor will call onNetworkConnected when in Wifi mode. 145 // Post time out event in case the NetworkMonitor doesn't call back. 146 mTimeoutRunnable = () -> finishTask(false); 147 mHandler.postDelayed(mTimeoutRunnable, RECONNECT_TIMEOUT_MS); 148 } 149 tryAddingNetwork(WifiConfiguration wifiConf)150 private int tryAddingNetwork(WifiConfiguration wifiConf) { 151 int netId = mWifiManager.addNetwork(wifiConf); 152 int retriesLeft = MAX_RETRIES; 153 int durationNextSleep = RETRY_SLEEP_DURATION_BASE_MS; 154 155 while(netId == -1 && retriesLeft > 0) { 156 ProvisionLogger.loge("Retrying in " + durationNextSleep + " ms."); 157 try { 158 mInjector.threadSleep(durationNextSleep); 159 } catch (InterruptedException e) { 160 ProvisionLogger.loge("Retry interrupted."); 161 } 162 durationNextSleep *= RETRY_SLEEP_MULTIPLIER; 163 retriesLeft--; 164 netId = mWifiManager.addNetwork(wifiConf); 165 } 166 return netId; 167 } 168 enableWifi()169 private boolean enableWifi() { 170 return mWifiManager.isWifiEnabled() || mWifiManager.setWifiEnabled(true); 171 } 172 173 @Override onNetworkConnected()174 public void onNetworkConnected() { 175 ProvisionLogger.logd("onNetworkConnected"); 176 if (isConnectedToSpecifiedWifi()) { 177 ProvisionLogger.logd("Connected to the correct network"); 178 finishTask(true); 179 // Remove time out callback. 180 mHandler.removeCallbacks(mTimeoutRunnable); 181 } 182 } 183 finishTask(boolean isSuccess)184 private synchronized void finishTask(boolean isSuccess) { 185 if (mTaskDone) { 186 return; 187 } 188 189 mTaskDone = true; 190 mNetworkMonitor.stopListening(); 191 if (isSuccess) { 192 success(); 193 } else { 194 error(0); 195 } 196 } 197 isConnectedToSpecifiedWifi()198 private boolean isConnectedToSpecifiedWifi() { 199 if (!mUtils.isConnectedToWifi(mContext)) { 200 ProvisionLogger.logd("Not connected to WIFI"); 201 return false; 202 } 203 WifiInfo connectionInfo = mWifiManager.getConnectionInfo(); 204 if (connectionInfo == null) { 205 ProvisionLogger.logd("connection info is null"); 206 return false; 207 } 208 String connectedSSID = mWifiManager.getConnectionInfo().getSSID(); 209 if (!mProvisioningParams.wifiInfo.ssid.equals(connectedSSID)) { 210 ProvisionLogger.logd("Wanted to connect SSID " + mProvisioningParams.wifiInfo.ssid 211 + ", but it is now connected to " + connectedSSID); 212 return false; 213 } 214 return true; 215 } 216 217 @VisibleForTesting 218 static class Injector { threadSleep(long milliseconds)219 public void threadSleep(long milliseconds) throws InterruptedException { 220 Thread.sleep(milliseconds); 221 } 222 } 223 } 224