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