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 android.content.Context; 20 import android.content.Intent; 21 import android.net.ConnectivityManager; 22 import android.net.NetworkInfo; 23 import android.net.wifi.WifiManager; 24 import android.os.Handler; 25 import android.os.HandlerThread; 26 import android.os.Looper; 27 import android.support.annotation.Nullable; 28 import android.text.TextUtils; 29 30 import java.lang.Thread; 31 32 import com.android.managedprovisioning.NetworkMonitor; 33 import com.android.managedprovisioning.ProvisionLogger; 34 import com.android.managedprovisioning.WifiConfig; 35 import com.android.managedprovisioning.common.Utils; 36 import com.android.managedprovisioning.model.WifiInfo; 37 38 /** 39 * Adds a wifi network to system. 40 */ 41 public class AddWifiNetworkTask implements NetworkMonitor.Callback { 42 private static final int RETRY_SLEEP_DURATION_BASE_MS = 500; 43 private static final int RETRY_SLEEP_MULTIPLIER = 2; 44 private static final int MAX_RETRIES = 6; 45 private static final int RECONNECT_TIMEOUT_MS = 60000; 46 47 private final Context mContext; 48 @Nullable 49 private final WifiInfo mWifiInfo; 50 private final Callback mCallback; 51 52 private WifiManager mWifiManager; 53 private NetworkMonitor mNetworkMonitor; 54 private WifiConfig mWifiConfig; 55 56 private Handler mHandler; 57 private boolean mTaskDone = false; 58 59 private int mDurationNextSleep = RETRY_SLEEP_DURATION_BASE_MS; 60 private int mRetriesLeft = MAX_RETRIES; 61 62 private final Utils mUtils = new Utils(); 63 64 /** 65 * @throws IllegalArgumentException if the {@code ssid} parameter is empty. 66 */ 67 public AddWifiNetworkTask(Context context, WifiInfo wifiInfo, Callback callback) { 68 mCallback = callback; 69 mContext = context; 70 mWifiInfo = wifiInfo; 71 mWifiManager = (WifiManager) mContext.getSystemService(Context.WIFI_SERVICE); 72 mWifiConfig = new WifiConfig(mWifiManager); 73 74 HandlerThread thread = new HandlerThread("Timeout thread", 75 android.os.Process.THREAD_PRIORITY_BACKGROUND); 76 thread.start(); 77 Looper looper = thread.getLooper(); 78 mHandler = new Handler(looper); 79 } 80 81 public void run() { 82 if (mWifiInfo == null) { 83 mCallback.onSuccess(); 84 return; 85 } 86 if (!enableWifi()) { 87 ProvisionLogger.loge("Failed to enable wifi"); 88 mCallback.onError(); 89 return; 90 } 91 92 if (isConnectedToSpecifiedWifi()) { 93 mCallback.onSuccess(); 94 return; 95 } 96 97 mNetworkMonitor = new NetworkMonitor(mContext, this); 98 connectToProvidedNetwork(); 99 } 100 101 private void connectToProvidedNetwork() { 102 int netId = mWifiConfig.addNetwork(mWifiInfo.ssid, mWifiInfo.hidden, mWifiInfo.securityType, 103 mWifiInfo.password, mWifiInfo.proxyHost, mWifiInfo.proxyPort, 104 mWifiInfo.proxyBypassHosts, mWifiInfo.pacUrl); 105 106 if (netId == -1) { 107 ProvisionLogger.loge("Failed to save network."); 108 if (mRetriesLeft > 0) { 109 ProvisionLogger.loge("Retrying in " + mDurationNextSleep + " ms."); 110 try { 111 Thread.sleep(mDurationNextSleep); 112 } catch (InterruptedException e) { 113 ProvisionLogger.loge("Retry interrupted."); 114 } 115 mDurationNextSleep *= RETRY_SLEEP_MULTIPLIER; 116 mRetriesLeft--; 117 connectToProvidedNetwork(); 118 return; 119 } else { 120 ProvisionLogger.loge("Already retried " + MAX_RETRIES + " times." 121 + " Quit retrying and report error."); 122 mCallback.onError(); 123 return; 124 } 125 } 126 127 // Network was successfully saved, now connect to it. 128 if (!mWifiManager.reconnect()) { 129 ProvisionLogger.loge("Unable to connect to wifi"); 130 mCallback.onError(); 131 return; 132 } 133 134 // NetworkMonitor will call onNetworkConnected when in Wifi mode. 135 // Post time out event in case the NetworkMonitor doesn't call back. 136 mHandler.postDelayed(new Runnable() { 137 public void run(){ 138 synchronized(this) { 139 if (mTaskDone) return; 140 mTaskDone = true; 141 } 142 ProvisionLogger.loge("Setting up wifi connection timed out."); 143 mCallback.onError(); 144 return; 145 } 146 }, RECONNECT_TIMEOUT_MS); 147 } 148 149 private boolean enableWifi() { 150 return mWifiManager != null 151 && (mWifiManager.isWifiEnabled() || mWifiManager.setWifiEnabled(true)); 152 } 153 154 @Override 155 public void onNetworkConnected() { 156 if (isConnectedToSpecifiedWifi()) { 157 synchronized(this) { 158 if (mTaskDone) return; 159 mTaskDone = true; 160 } 161 162 ProvisionLogger.logd("Connected to the correct network"); 163 164 // Remove time out callback. 165 mHandler.removeCallbacksAndMessages(null); 166 167 cleanUp(); 168 mCallback.onSuccess(); 169 return; 170 } 171 } 172 173 @Override 174 public void onNetworkDisconnected() { 175 176 } 177 178 public void cleanUp() { 179 if (mNetworkMonitor != null) { 180 mNetworkMonitor.close(); 181 mNetworkMonitor = null; 182 } 183 } 184 185 private boolean isConnectedToSpecifiedWifi() { 186 return mUtils.isConnectedToWifi(mContext) 187 && mWifiManager != null 188 && mWifiManager.getConnectionInfo() != null 189 && mWifiInfo.ssid.equals(mWifiManager.getConnectionInfo().getSSID()); 190 } 191 192 public abstract static class Callback { 193 public abstract void onSuccess(); 194 public abstract void onError(); 195 } 196 } 197