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.ConnectivityManager; 23 import android.net.wifi.WifiConfiguration; 24 import android.net.wifi.WifiInfo; 25 import android.net.wifi.WifiManager; 26 import android.os.Handler; 27 28 import com.android.internal.annotations.VisibleForTesting; 29 import com.android.managedprovisioning.analytics.MetricsWriterFactory; 30 import com.android.managedprovisioning.analytics.ProvisioningAnalyticsTracker; 31 import com.android.managedprovisioning.common.ManagedProvisioningSharedPreferences; 32 import com.android.managedprovisioning.common.ProvisionLogger; 33 import com.android.managedprovisioning.common.SettingsFacade; 34 import com.android.managedprovisioning.common.Utils; 35 import com.android.managedprovisioning.model.ProvisioningParams; 36 import com.android.managedprovisioning.task.wifi.NetworkMonitor; 37 import com.android.managedprovisioning.task.wifi.WifiConfigurationProvider; 38 39 /** 40 * Adds a wifi network to the system and waits for it to successfully connect. If the system does 41 * not support wifi, the adding or connection times out {@link #error(int)} will be called. 42 */ 43 public class AddWifiNetworkTask extends AbstractProvisioningTask 44 implements NetworkMonitor.NetworkConnectedCallback { 45 private static final int RETRY_SLEEP_DURATION_BASE_MS = 500; 46 private static final int RETRY_SLEEP_MULTIPLIER = 2; 47 private static final int MAX_RETRIES = 6; 48 private static final int RECONNECT_TIMEOUT_MS = 60000; 49 @VisibleForTesting static final int ADD_NETWORK_FAIL = -1; 50 51 private final WifiConfigurationProvider mWifiConfigurationProvider; 52 private final WifiManager mWifiManager; 53 private final NetworkMonitor mNetworkMonitor; 54 55 private Handler mHandler; 56 private boolean mTaskDone = false; 57 58 private final Utils mUtils; 59 private Runnable mTimeoutRunnable; 60 private Injector mInjector; 61 AddWifiNetworkTask( Context context, ProvisioningParams provisioningParams, Callback callback)62 public AddWifiNetworkTask( 63 Context context, 64 ProvisioningParams provisioningParams, 65 Callback callback) { 66 this( 67 new NetworkMonitor(context, /* waitForValidated */ false), 68 new WifiConfigurationProvider(), 69 context, provisioningParams, callback, new Utils(), new Injector(), 70 new ProvisioningAnalyticsTracker( 71 MetricsWriterFactory.getMetricsWriter(context, new SettingsFacade()), 72 new ManagedProvisioningSharedPreferences(context))); 73 } 74 75 @VisibleForTesting AddWifiNetworkTask( NetworkMonitor networkMonitor, WifiConfigurationProvider wifiConfigurationProvider, Context context, ProvisioningParams provisioningParams, Callback callback, Utils utils, Injector injector, ProvisioningAnalyticsTracker provisioningAnalyticsTracker)76 AddWifiNetworkTask( 77 NetworkMonitor networkMonitor, 78 WifiConfigurationProvider wifiConfigurationProvider, 79 Context context, 80 ProvisioningParams provisioningParams, 81 Callback callback, 82 Utils utils, 83 Injector injector, 84 ProvisioningAnalyticsTracker provisioningAnalyticsTracker) { 85 super(context, provisioningParams, callback, provisioningAnalyticsTracker); 86 87 mNetworkMonitor = checkNotNull(networkMonitor); 88 mWifiConfigurationProvider = checkNotNull(wifiConfigurationProvider); 89 mWifiManager = (WifiManager) context.getSystemService(Context.WIFI_SERVICE); 90 mUtils = checkNotNull(utils); 91 mInjector = checkNotNull(injector); 92 } 93 94 @Override run(int userId)95 public void run(int userId) { 96 if (mProvisioningParams.wifiInfo == null) { 97 success(); 98 return; 99 } 100 101 if (mWifiManager == null || !enableWifi()) { 102 ProvisionLogger.loge("Failed to enable wifi"); 103 error(0); 104 return; 105 } 106 107 if (isConnectedToSpecifiedWifi()) { 108 success(); 109 return; 110 } 111 112 mTaskDone = false; 113 mHandler = new Handler(); 114 mNetworkMonitor.startListening(this); 115 connectToProvidedNetwork(); 116 } 117 connectToProvidedNetwork()118 private void connectToProvidedNetwork() { 119 WifiConfiguration wifiConf = 120 mWifiConfigurationProvider.generateWifiConfiguration(mProvisioningParams.wifiInfo); 121 122 if (wifiConf == null) { 123 ProvisionLogger.loge("WifiConfiguration is null"); 124 error(0); 125 return; 126 } 127 128 int netId = tryAddingNetwork(wifiConf); 129 130 if (netId == ADD_NETWORK_FAIL) { 131 ProvisionLogger.loge("Unable to add network after trying " + MAX_RETRIES + " times."); 132 error(0); 133 return; 134 } 135 136 // Setting disableOthers to 'true' should trigger a connection attempt. 137 mWifiManager.enableNetwork(netId, true); 138 mWifiManager.saveConfiguration(); 139 140 // Network was successfully saved, now connect to it. 141 if (!mWifiManager.reconnect()) { 142 ProvisionLogger.loge("Unable to connect to wifi"); 143 error(0); 144 return; 145 } 146 147 // NetworkMonitor will call onNetworkConnected when in Wifi mode. 148 // Post time out event in case the NetworkMonitor doesn't call back. 149 mTimeoutRunnable = () -> finishTask(false); 150 mHandler.postDelayed(mTimeoutRunnable, RECONNECT_TIMEOUT_MS); 151 } 152 tryAddingNetwork(WifiConfiguration wifiConf)153 private int tryAddingNetwork(WifiConfiguration wifiConf) { 154 int netId = mWifiManager.addNetwork(wifiConf); 155 int retriesLeft = MAX_RETRIES; 156 int durationNextSleep = RETRY_SLEEP_DURATION_BASE_MS; 157 158 while(netId == -1 && retriesLeft > 0) { 159 ProvisionLogger.loge("Retrying in " + durationNextSleep + " ms."); 160 try { 161 mInjector.threadSleep(durationNextSleep); 162 } catch (InterruptedException e) { 163 ProvisionLogger.loge("Retry interrupted."); 164 } 165 durationNextSleep *= RETRY_SLEEP_MULTIPLIER; 166 retriesLeft--; 167 netId = mWifiManager.addNetwork(wifiConf); 168 } 169 return netId; 170 } 171 enableWifi()172 private boolean enableWifi() { 173 return mWifiManager.isWifiEnabled() || mWifiManager.setWifiEnabled(true); 174 } 175 176 @Override onNetworkConnected()177 public void onNetworkConnected() { 178 ProvisionLogger.logd("onNetworkConnected"); 179 if (isConnectedToSpecifiedWifi()) { 180 ProvisionLogger.logd("Connected to the correct network"); 181 finishTask(true); 182 // Remove time out callback. 183 mHandler.removeCallbacks(mTimeoutRunnable); 184 } 185 } 186 finishTask(boolean isSuccess)187 private synchronized void finishTask(boolean isSuccess) { 188 if (mTaskDone) { 189 return; 190 } 191 192 mTaskDone = true; 193 mNetworkMonitor.stopListening(); 194 if (isSuccess) { 195 success(); 196 } else { 197 error(0); 198 } 199 } 200 isConnectedToSpecifiedWifi()201 private boolean isConnectedToSpecifiedWifi() { 202 if (!mUtils.isNetworkTypeConnected(mContext, ConnectivityManager.TYPE_WIFI)) { 203 ProvisionLogger.logd("Not connected to WIFI"); 204 return false; 205 } 206 WifiInfo connectionInfo = mWifiManager.getConnectionInfo(); 207 if (connectionInfo == null) { 208 ProvisionLogger.logd("connection info is null"); 209 return false; 210 } 211 String connectedSSID = mWifiManager.getConnectionInfo().getSSID(); 212 if (!mProvisioningParams.wifiInfo.ssid.equals(connectedSSID)) { 213 ProvisionLogger.logd("Wanted to connect SSID " + mProvisioningParams.wifiInfo.ssid 214 + ", but it is now connected to " + connectedSSID); 215 return false; 216 } 217 return true; 218 } 219 220 @VisibleForTesting 221 static class Injector { threadSleep(long milliseconds)222 public void threadSleep(long milliseconds) throws InterruptedException { 223 Thread.sleep(milliseconds); 224 } 225 } 226 } 227