1c775738a923c5cb9db3ad558b4cba85ee84b0e4dSander Alewijnse/*
2c775738a923c5cb9db3ad558b4cba85ee84b0e4dSander Alewijnse * Copyright 2014, The Android Open Source Project
3c775738a923c5cb9db3ad558b4cba85ee84b0e4dSander Alewijnse *
4c775738a923c5cb9db3ad558b4cba85ee84b0e4dSander Alewijnse * Licensed under the Apache License, Version 2.0 (the "License");
5c775738a923c5cb9db3ad558b4cba85ee84b0e4dSander Alewijnse * you may not use this file except in compliance with the License.
6c775738a923c5cb9db3ad558b4cba85ee84b0e4dSander Alewijnse * You may obtain a copy of the License at
7c775738a923c5cb9db3ad558b4cba85ee84b0e4dSander Alewijnse *
8c775738a923c5cb9db3ad558b4cba85ee84b0e4dSander Alewijnse *     http://www.apache.org/licenses/LICENSE-2.0
9c775738a923c5cb9db3ad558b4cba85ee84b0e4dSander Alewijnse *
10c775738a923c5cb9db3ad558b4cba85ee84b0e4dSander Alewijnse * Unless required by applicable law or agreed to in writing, software
11c775738a923c5cb9db3ad558b4cba85ee84b0e4dSander Alewijnse * distributed under the License is distributed on an "AS IS" BASIS,
12c775738a923c5cb9db3ad558b4cba85ee84b0e4dSander Alewijnse * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13c775738a923c5cb9db3ad558b4cba85ee84b0e4dSander Alewijnse * See the License for the specific language governing permissions and
14c775738a923c5cb9db3ad558b4cba85ee84b0e4dSander Alewijnse * limitations under the License.
15c775738a923c5cb9db3ad558b4cba85ee84b0e4dSander Alewijnse */
16c775738a923c5cb9db3ad558b4cba85ee84b0e4dSander Alewijnse
17c775738a923c5cb9db3ad558b4cba85ee84b0e4dSander Alewijnsepackage com.android.managedprovisioning.task;
18c775738a923c5cb9db3ad558b4cba85ee84b0e4dSander Alewijnse
19c775738a923c5cb9db3ad558b4cba85ee84b0e4dSander Alewijnseimport android.content.Context;
20326bcfdd72218a2d3b35d8e1ca80b8e36263402eSander Alewijnseimport android.content.Intent;
21326bcfdd72218a2d3b35d8e1ca80b8e36263402eSander Alewijnseimport android.net.ConnectivityManager;
22326bcfdd72218a2d3b35d8e1ca80b8e36263402eSander Alewijnseimport android.net.NetworkInfo;
23c775738a923c5cb9db3ad558b4cba85ee84b0e4dSander Alewijnseimport android.net.wifi.WifiManager;
244b2782a61e68948b6f20a4a3139f7c8eb77de265Sander Alewijnseimport android.os.Handler;
254b2782a61e68948b6f20a4a3139f7c8eb77de265Sander Alewijnseimport android.os.HandlerThread;
264b2782a61e68948b6f20a4a3139f7c8eb77de265Sander Alewijnseimport android.os.Looper;
2777bac595693aa1aa6e4832f278d5b3b35a3ffd53Steven Ngimport android.support.annotation.Nullable;
28c775738a923c5cb9db3ad558b4cba85ee84b0e4dSander Alewijnseimport android.text.TextUtils;
29c775738a923c5cb9db3ad558b4cba85ee84b0e4dSander Alewijnse
30a09c2cf61a19a642a86b1032cb50a61d2630c564Sander Alewijnseimport java.lang.Thread;
31a09c2cf61a19a642a86b1032cb50a61d2630c564Sander Alewijnse
32c775738a923c5cb9db3ad558b4cba85ee84b0e4dSander Alewijnseimport com.android.managedprovisioning.NetworkMonitor;
33c775738a923c5cb9db3ad558b4cba85ee84b0e4dSander Alewijnseimport com.android.managedprovisioning.ProvisionLogger;
34c775738a923c5cb9db3ad558b4cba85ee84b0e4dSander Alewijnseimport com.android.managedprovisioning.WifiConfig;
35d1784bd4d917bb36125e6faf125a2425c343838bSteven Ngimport com.android.managedprovisioning.common.Utils;
36d1784bd4d917bb36125e6faf125a2425c343838bSteven Ngimport com.android.managedprovisioning.model.WifiInfo;
3774d6c14da5117ffd3458602f9f6946c531143436Sander Alewijnse
38c775738a923c5cb9db3ad558b4cba85ee84b0e4dSander Alewijnse/**
39c775738a923c5cb9db3ad558b4cba85ee84b0e4dSander Alewijnse * Adds a wifi network to system.
40c775738a923c5cb9db3ad558b4cba85ee84b0e4dSander Alewijnse */
41c775738a923c5cb9db3ad558b4cba85ee84b0e4dSander Alewijnsepublic class AddWifiNetworkTask implements NetworkMonitor.Callback {
42a09c2cf61a19a642a86b1032cb50a61d2630c564Sander Alewijnse    private static final int RETRY_SLEEP_DURATION_BASE_MS = 500;
43a09c2cf61a19a642a86b1032cb50a61d2630c564Sander Alewijnse    private static final int RETRY_SLEEP_MULTIPLIER = 2;
44a09c2cf61a19a642a86b1032cb50a61d2630c564Sander Alewijnse    private static final int MAX_RETRIES = 6;
45663445f060e9226a235e43f3b30c60bf9aa9d84eJukka Zitting    private static final int RECONNECT_TIMEOUT_MS = 60000;
46a09c2cf61a19a642a86b1032cb50a61d2630c564Sander Alewijnse
4728bffd6424a3d9f2dbefe6c7d6144f3a4edee3bcSander Alewijnse    private final Context mContext;
4877bac595693aa1aa6e4832f278d5b3b35a3ffd53Steven Ng    @Nullable
4974d6c14da5117ffd3458602f9f6946c531143436Sander Alewijnse    private final WifiInfo mWifiInfo;
5028bffd6424a3d9f2dbefe6c7d6144f3a4edee3bcSander Alewijnse    private final Callback mCallback;
5128bffd6424a3d9f2dbefe6c7d6144f3a4edee3bcSander Alewijnse
52c775738a923c5cb9db3ad558b4cba85ee84b0e4dSander Alewijnse    private WifiManager mWifiManager;
5302b28903a2695617866138c5ebe23a8c66ee9714Sander Alewijnse    private NetworkMonitor mNetworkMonitor;
54a09c2cf61a19a642a86b1032cb50a61d2630c564Sander Alewijnse    private WifiConfig mWifiConfig;
55a09c2cf61a19a642a86b1032cb50a61d2630c564Sander Alewijnse
564b2782a61e68948b6f20a4a3139f7c8eb77de265Sander Alewijnse    private Handler mHandler;
574b2782a61e68948b6f20a4a3139f7c8eb77de265Sander Alewijnse    private boolean mTaskDone = false;
584b2782a61e68948b6f20a4a3139f7c8eb77de265Sander Alewijnse
59a09c2cf61a19a642a86b1032cb50a61d2630c564Sander Alewijnse    private int mDurationNextSleep = RETRY_SLEEP_DURATION_BASE_MS;
60a09c2cf61a19a642a86b1032cb50a61d2630c564Sander Alewijnse    private int mRetriesLeft = MAX_RETRIES;
61c775738a923c5cb9db3ad558b4cba85ee84b0e4dSander Alewijnse
62ea821b26fc845efa8058c883b0210432e9619f77Benjamin Franz    private final Utils mUtils = new Utils();
63ea821b26fc845efa8058c883b0210432e9619f77Benjamin Franz
644b2782a61e68948b6f20a4a3139f7c8eb77de265Sander Alewijnse    /**
654b2782a61e68948b6f20a4a3139f7c8eb77de265Sander Alewijnse     * @throws IllegalArgumentException if the {@code ssid} parameter is empty.
664b2782a61e68948b6f20a4a3139f7c8eb77de265Sander Alewijnse     */
6774d6c14da5117ffd3458602f9f6946c531143436Sander Alewijnse    public AddWifiNetworkTask(Context context, WifiInfo wifiInfo, Callback callback) {
6828bffd6424a3d9f2dbefe6c7d6144f3a4edee3bcSander Alewijnse        mCallback = callback;
69c775738a923c5cb9db3ad558b4cba85ee84b0e4dSander Alewijnse        mContext = context;
7074d6c14da5117ffd3458602f9f6946c531143436Sander Alewijnse        mWifiInfo = wifiInfo;
71c775738a923c5cb9db3ad558b4cba85ee84b0e4dSander Alewijnse        mWifiManager  = (WifiManager) mContext.getSystemService(Context.WIFI_SERVICE);
72a09c2cf61a19a642a86b1032cb50a61d2630c564Sander Alewijnse        mWifiConfig = new WifiConfig(mWifiManager);
734b2782a61e68948b6f20a4a3139f7c8eb77de265Sander Alewijnse
744b2782a61e68948b6f20a4a3139f7c8eb77de265Sander Alewijnse        HandlerThread thread = new HandlerThread("Timeout thread",
754b2782a61e68948b6f20a4a3139f7c8eb77de265Sander Alewijnse                android.os.Process.THREAD_PRIORITY_BACKGROUND);
764b2782a61e68948b6f20a4a3139f7c8eb77de265Sander Alewijnse        thread.start();
774b2782a61e68948b6f20a4a3139f7c8eb77de265Sander Alewijnse        Looper looper = thread.getLooper();
784b2782a61e68948b6f20a4a3139f7c8eb77de265Sander Alewijnse        mHandler = new Handler(looper);
79c775738a923c5cb9db3ad558b4cba85ee84b0e4dSander Alewijnse    }
80c775738a923c5cb9db3ad558b4cba85ee84b0e4dSander Alewijnse
81c775738a923c5cb9db3ad558b4cba85ee84b0e4dSander Alewijnse    public void run() {
8277bac595693aa1aa6e4832f278d5b3b35a3ffd53Steven Ng        if (mWifiInfo == null) {
83ccd60165065172486fff32b581a0fb4d724b2991Julia Reynolds            mCallback.onSuccess();
84ccd60165065172486fff32b581a0fb4d724b2991Julia Reynolds            return;
85ccd60165065172486fff32b581a0fb4d724b2991Julia Reynolds        }
86c775738a923c5cb9db3ad558b4cba85ee84b0e4dSander Alewijnse        if (!enableWifi()) {
87c775738a923c5cb9db3ad558b4cba85ee84b0e4dSander Alewijnse            ProvisionLogger.loge("Failed to enable wifi");
88c775738a923c5cb9db3ad558b4cba85ee84b0e4dSander Alewijnse            mCallback.onError();
89c775738a923c5cb9db3ad558b4cba85ee84b0e4dSander Alewijnse            return;
90c775738a923c5cb9db3ad558b4cba85ee84b0e4dSander Alewijnse        }
91c775738a923c5cb9db3ad558b4cba85ee84b0e4dSander Alewijnse
924b2782a61e68948b6f20a4a3139f7c8eb77de265Sander Alewijnse        if (isConnectedToSpecifiedWifi()) {
93326bcfdd72218a2d3b35d8e1ca80b8e36263402eSander Alewijnse            mCallback.onSuccess();
944b2782a61e68948b6f20a4a3139f7c8eb77de265Sander Alewijnse            return;
95326bcfdd72218a2d3b35d8e1ca80b8e36263402eSander Alewijnse        }
964b2782a61e68948b6f20a4a3139f7c8eb77de265Sander Alewijnse
974b2782a61e68948b6f20a4a3139f7c8eb77de265Sander Alewijnse        mNetworkMonitor = new NetworkMonitor(mContext, this);
984b2782a61e68948b6f20a4a3139f7c8eb77de265Sander Alewijnse        connectToProvidedNetwork();
99326bcfdd72218a2d3b35d8e1ca80b8e36263402eSander Alewijnse    }
100326bcfdd72218a2d3b35d8e1ca80b8e36263402eSander Alewijnse
101326bcfdd72218a2d3b35d8e1ca80b8e36263402eSander Alewijnse    private void connectToProvidedNetwork() {
10274d6c14da5117ffd3458602f9f6946c531143436Sander Alewijnse        int netId = mWifiConfig.addNetwork(mWifiInfo.ssid, mWifiInfo.hidden, mWifiInfo.securityType,
10374d6c14da5117ffd3458602f9f6946c531143436Sander Alewijnse                mWifiInfo.password, mWifiInfo.proxyHost, mWifiInfo.proxyPort,
10474d6c14da5117ffd3458602f9f6946c531143436Sander Alewijnse                mWifiInfo.proxyBypassHosts, mWifiInfo.pacUrl);
105c775738a923c5cb9db3ad558b4cba85ee84b0e4dSander Alewijnse
106c775738a923c5cb9db3ad558b4cba85ee84b0e4dSander Alewijnse        if (netId == -1) {
107c775738a923c5cb9db3ad558b4cba85ee84b0e4dSander Alewijnse            ProvisionLogger.loge("Failed to save network.");
108a09c2cf61a19a642a86b1032cb50a61d2630c564Sander Alewijnse            if (mRetriesLeft > 0) {
109a09c2cf61a19a642a86b1032cb50a61d2630c564Sander Alewijnse                ProvisionLogger.loge("Retrying in " + mDurationNextSleep + " ms.");
110a09c2cf61a19a642a86b1032cb50a61d2630c564Sander Alewijnse                try {
111a09c2cf61a19a642a86b1032cb50a61d2630c564Sander Alewijnse                    Thread.sleep(mDurationNextSleep);
112a09c2cf61a19a642a86b1032cb50a61d2630c564Sander Alewijnse                } catch (InterruptedException e) {
113a09c2cf61a19a642a86b1032cb50a61d2630c564Sander Alewijnse                    ProvisionLogger.loge("Retry interrupted.");
114a09c2cf61a19a642a86b1032cb50a61d2630c564Sander Alewijnse                }
115a09c2cf61a19a642a86b1032cb50a61d2630c564Sander Alewijnse                mDurationNextSleep *= RETRY_SLEEP_MULTIPLIER;
116a09c2cf61a19a642a86b1032cb50a61d2630c564Sander Alewijnse                mRetriesLeft--;
117a09c2cf61a19a642a86b1032cb50a61d2630c564Sander Alewijnse                connectToProvidedNetwork();
118a09c2cf61a19a642a86b1032cb50a61d2630c564Sander Alewijnse                return;
119a09c2cf61a19a642a86b1032cb50a61d2630c564Sander Alewijnse            } else {
120a09c2cf61a19a642a86b1032cb50a61d2630c564Sander Alewijnse                ProvisionLogger.loge("Already retried " +  MAX_RETRIES + " times."
121a09c2cf61a19a642a86b1032cb50a61d2630c564Sander Alewijnse                        + " Quit retrying and report error.");
122a09c2cf61a19a642a86b1032cb50a61d2630c564Sander Alewijnse                mCallback.onError();
123a09c2cf61a19a642a86b1032cb50a61d2630c564Sander Alewijnse                return;
124a09c2cf61a19a642a86b1032cb50a61d2630c564Sander Alewijnse            }
1254b2782a61e68948b6f20a4a3139f7c8eb77de265Sander Alewijnse        }
1264b2782a61e68948b6f20a4a3139f7c8eb77de265Sander Alewijnse
1274b2782a61e68948b6f20a4a3139f7c8eb77de265Sander Alewijnse        // Network was successfully saved, now connect to it.
1284b2782a61e68948b6f20a4a3139f7c8eb77de265Sander Alewijnse        if (!mWifiManager.reconnect()) {
1294b2782a61e68948b6f20a4a3139f7c8eb77de265Sander Alewijnse            ProvisionLogger.loge("Unable to connect to wifi");
1304b2782a61e68948b6f20a4a3139f7c8eb77de265Sander Alewijnse            mCallback.onError();
1314b2782a61e68948b6f20a4a3139f7c8eb77de265Sander Alewijnse            return;
132c775738a923c5cb9db3ad558b4cba85ee84b0e4dSander Alewijnse        }
133c775738a923c5cb9db3ad558b4cba85ee84b0e4dSander Alewijnse
134326bcfdd72218a2d3b35d8e1ca80b8e36263402eSander Alewijnse        // NetworkMonitor will call onNetworkConnected when in Wifi mode.
1354b2782a61e68948b6f20a4a3139f7c8eb77de265Sander Alewijnse        // Post time out event in case the NetworkMonitor doesn't call back.
1364b2782a61e68948b6f20a4a3139f7c8eb77de265Sander Alewijnse        mHandler.postDelayed(new Runnable() {
1374b2782a61e68948b6f20a4a3139f7c8eb77de265Sander Alewijnse                public void run(){
1384b2782a61e68948b6f20a4a3139f7c8eb77de265Sander Alewijnse                    synchronized(this) {
1394b2782a61e68948b6f20a4a3139f7c8eb77de265Sander Alewijnse                        if (mTaskDone) return;
1404b2782a61e68948b6f20a4a3139f7c8eb77de265Sander Alewijnse                        mTaskDone = true;
1414b2782a61e68948b6f20a4a3139f7c8eb77de265Sander Alewijnse                    }
1424b2782a61e68948b6f20a4a3139f7c8eb77de265Sander Alewijnse                    ProvisionLogger.loge("Setting up wifi connection timed out.");
1434b2782a61e68948b6f20a4a3139f7c8eb77de265Sander Alewijnse                    mCallback.onError();
1444b2782a61e68948b6f20a4a3139f7c8eb77de265Sander Alewijnse                    return;
1454b2782a61e68948b6f20a4a3139f7c8eb77de265Sander Alewijnse                }
1464b2782a61e68948b6f20a4a3139f7c8eb77de265Sander Alewijnse            }, RECONNECT_TIMEOUT_MS);
147c775738a923c5cb9db3ad558b4cba85ee84b0e4dSander Alewijnse    }
148c775738a923c5cb9db3ad558b4cba85ee84b0e4dSander Alewijnse
149c775738a923c5cb9db3ad558b4cba85ee84b0e4dSander Alewijnse    private boolean enableWifi() {
1504b2782a61e68948b6f20a4a3139f7c8eb77de265Sander Alewijnse        return mWifiManager != null
1514b2782a61e68948b6f20a4a3139f7c8eb77de265Sander Alewijnse                && (mWifiManager.isWifiEnabled() || mWifiManager.setWifiEnabled(true));
152c775738a923c5cb9db3ad558b4cba85ee84b0e4dSander Alewijnse    }
153c775738a923c5cb9db3ad558b4cba85ee84b0e4dSander Alewijnse
154c775738a923c5cb9db3ad558b4cba85ee84b0e4dSander Alewijnse    @Override
155c775738a923c5cb9db3ad558b4cba85ee84b0e4dSander Alewijnse    public void onNetworkConnected() {
1564b2782a61e68948b6f20a4a3139f7c8eb77de265Sander Alewijnse        if (isConnectedToSpecifiedWifi()) {
1574b2782a61e68948b6f20a4a3139f7c8eb77de265Sander Alewijnse            synchronized(this) {
1584b2782a61e68948b6f20a4a3139f7c8eb77de265Sander Alewijnse                if (mTaskDone) return;
1594b2782a61e68948b6f20a4a3139f7c8eb77de265Sander Alewijnse                mTaskDone = true;
1604b2782a61e68948b6f20a4a3139f7c8eb77de265Sander Alewijnse            }
1614b2782a61e68948b6f20a4a3139f7c8eb77de265Sander Alewijnse
162c775738a923c5cb9db3ad558b4cba85ee84b0e4dSander Alewijnse            ProvisionLogger.logd("Connected to the correct network");
1634b2782a61e68948b6f20a4a3139f7c8eb77de265Sander Alewijnse
1644b2782a61e68948b6f20a4a3139f7c8eb77de265Sander Alewijnse            // Remove time out callback.
1654b2782a61e68948b6f20a4a3139f7c8eb77de265Sander Alewijnse            mHandler.removeCallbacksAndMessages(null);
1664b2782a61e68948b6f20a4a3139f7c8eb77de265Sander Alewijnse
1674b2782a61e68948b6f20a4a3139f7c8eb77de265Sander Alewijnse            cleanUp();
16802b28903a2695617866138c5ebe23a8c66ee9714Sander Alewijnse            mCallback.onSuccess();
1694b2782a61e68948b6f20a4a3139f7c8eb77de265Sander Alewijnse            return;
170c775738a923c5cb9db3ad558b4cba85ee84b0e4dSander Alewijnse        }
171c775738a923c5cb9db3ad558b4cba85ee84b0e4dSander Alewijnse    }
172c775738a923c5cb9db3ad558b4cba85ee84b0e4dSander Alewijnse
173326bcfdd72218a2d3b35d8e1ca80b8e36263402eSander Alewijnse    @Override
174326bcfdd72218a2d3b35d8e1ca80b8e36263402eSander Alewijnse    public void onNetworkDisconnected() {
175326bcfdd72218a2d3b35d8e1ca80b8e36263402eSander Alewijnse
176326bcfdd72218a2d3b35d8e1ca80b8e36263402eSander Alewijnse    }
177326bcfdd72218a2d3b35d8e1ca80b8e36263402eSander Alewijnse
178d70438542632a1c8df15bdd9d91bfee52bf2b655Sander Alewijnse    public void cleanUp() {
17928bffd6424a3d9f2dbefe6c7d6144f3a4edee3bcSander Alewijnse        if (mNetworkMonitor != null) {
18028bffd6424a3d9f2dbefe6c7d6144f3a4edee3bcSander Alewijnse            mNetworkMonitor.close();
18128bffd6424a3d9f2dbefe6c7d6144f3a4edee3bcSander Alewijnse            mNetworkMonitor = null;
18228bffd6424a3d9f2dbefe6c7d6144f3a4edee3bcSander Alewijnse        }
18328bffd6424a3d9f2dbefe6c7d6144f3a4edee3bcSander Alewijnse    }
18428bffd6424a3d9f2dbefe6c7d6144f3a4edee3bcSander Alewijnse
1854b2782a61e68948b6f20a4a3139f7c8eb77de265Sander Alewijnse    private boolean isConnectedToSpecifiedWifi() {
186ea821b26fc845efa8058c883b0210432e9619f77Benjamin Franz        return mUtils.isConnectedToWifi(mContext)
187afc4e4710d5170cf3fd07d23aaa445b21ee5c782Sudheer Shanka                && mWifiManager != null
1884b2782a61e68948b6f20a4a3139f7c8eb77de265Sander Alewijnse                && mWifiManager.getConnectionInfo() != null
18974d6c14da5117ffd3458602f9f6946c531143436Sander Alewijnse                && mWifiInfo.ssid.equals(mWifiManager.getConnectionInfo().getSSID());
1904b2782a61e68948b6f20a4a3139f7c8eb77de265Sander Alewijnse    }
1914b2782a61e68948b6f20a4a3139f7c8eb77de265Sander Alewijnse
192c775738a923c5cb9db3ad558b4cba85ee84b0e4dSander Alewijnse    public abstract static class Callback {
193c775738a923c5cb9db3ad558b4cba85ee84b0e4dSander Alewijnse        public abstract void onSuccess();
194c775738a923c5cb9db3ad558b4cba85ee84b0e4dSander Alewijnse        public abstract void onError();
195c775738a923c5cb9db3ad558b4cba85ee84b0e4dSander Alewijnse    }
1969643319ce223e09e7bb3450095939a92be00e3c4Sander Alewijnse}
197