blob: 12b7ceec1b0b2d641ce374593e37481c04ffad49 [file] [log] [blame]
/*
* 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.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.R;
import com.android.managedprovisioning.common.ProvisionLogger;
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),
new WifiConfigurationProvider(),
context, provisioningParams, callback, new Utils(), new Injector());
}
@VisibleForTesting
AddWifiNetworkTask(
NetworkMonitor networkMonitor,
WifiConfigurationProvider wifiConfigurationProvider,
Context context,
ProvisioningParams provisioningParams,
Callback callback,
Utils utils,
Injector injector) {
super(context, provisioningParams, callback);
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();
}
@Override
public int getStatusMsgId() {
return R.string.progress_connect_to_wifi;
}
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.isConnectedToWifi(mContext)) {
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);
}
}
}