1/*
2 * Copyright (C) 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
17package com.android.tv.settings.connectivity;
18
19import com.android.tv.settings.R;
20import com.android.tv.settings.form.FormPage;
21
22import android.content.Context;
23import android.net.IpConfiguration.IpAssignment;
24import android.net.IpConfiguration.ProxySettings;
25import android.net.IpConfiguration;
26import android.net.LinkAddress;
27import android.net.NetworkUtils;
28import android.net.ProxyInfo;
29import android.net.RouteInfo;
30import android.net.StaticIpConfiguration;
31import android.net.wifi.WifiConfiguration;
32import android.os.Bundle;
33import android.text.TextUtils;
34
35import java.net.InetAddress;
36import java.net.Inet4Address;
37import java.util.Collection;
38import java.util.Iterator;
39
40/**
41 * Handles the form page flow of setting advanced options.
42 */
43public class AdvancedWifiOptionsFlow {
44
45    public interface PageHandler {
46        void addPage(WifiFormPageType formPageType);
47
48        void removePage(FormPage formPage);
49
50        boolean choiceChosen(FormPage formPage, int choiceResourceId);
51    }
52
53    public static final int RESULT_UNKNOWN_PAGE = 0;
54    public static final int RESULT_PAGE_HANDLED = 1;
55    public static final int RESULT_ALL_PAGES_COMPLETE = 2;
56
57    private final Context mContext;
58    private final PageHandler mPageHandler;
59    private final boolean mAskFirst;
60    private final NetworkConfiguration mInitialConfiguration;
61    private FormPage mAdvancedOptionsPage;
62    private FormPage mProxySettingsPage;
63    private FormPage mIpSettingsPage;
64    private FormPage mProxyHostnamePage;
65    private FormPage mProxyPortPage;
66    private FormPage mProxyBypassPage;
67    private FormPage mIpAddressPage;
68    private FormPage mGatewayPage;
69    private FormPage mNetworkPrefixLengthPage;
70    private FormPage mDns1Page;
71    private FormPage mDns2Page;
72    private IpConfiguration mIpConfiguration;
73    private boolean mSettingsFlow;
74
75    public AdvancedWifiOptionsFlow(Context context, PageHandler pageHandler,
76            NetworkConfiguration initialConfiguration) {
77        mContext = context;
78        mPageHandler = pageHandler;
79        mAskFirst = false;
80        mSettingsFlow = true;
81        mInitialConfiguration = initialConfiguration;
82        mIpConfiguration = (initialConfiguration != null) ?
83                mInitialConfiguration.getIpConfiguration() :
84                new IpConfiguration();
85    }
86
87    public AdvancedWifiOptionsFlow(Context context, PageHandler pageHandler, boolean askFirst,
88            NetworkConfiguration initialConfiguration) {
89        mContext = context;
90        mPageHandler = pageHandler;
91        mAskFirst = askFirst;
92        mSettingsFlow = false;
93        mInitialConfiguration = initialConfiguration;
94        mIpConfiguration = (initialConfiguration != null) ?
95                mInitialConfiguration.getIpConfiguration() :
96                new IpConfiguration();
97    }
98
99    public WifiFormPageType getInitialPage() {
100        return (mAskFirst) ? WifiFormPageType.ADVANCED_OPTIONS : WifiFormPageType.PROXY_SETTINGS;
101    }
102
103    public WifiFormPageType getInitialProxySettingsPage() {
104        return WifiFormPageType.PROXY_SETTINGS;
105    }
106
107   public WifiFormPageType getInitialIpSettingsPage() {
108        return WifiFormPageType.IP_SETTINGS;
109    }
110
111    /**
112     * @param formPageType the type of page that completed.
113     * @param formPage the page that complete.
114     * @return RESULT_PAGE_HANDLED if the page has been handled.
115     *         RESULT_UNKNOWN_PAGE if the page is unrecognized and was ignored.
116     *         RESULT_ALL_PAGES_COMPLETE if all pages have been completed.
117     */
118    public int handlePageComplete(WifiFormPageType formPageType, FormPage formPage) {
119
120        switch (formPageType) {
121            case ADVANCED_OPTIONS:
122                mAdvancedOptionsPage = formPage;
123                if (mPageHandler.choiceChosen(formPage, R.string.wifi_action_advanced_no)) {
124                    processProxySettings();
125                    processIpSettings();
126                    return RESULT_ALL_PAGES_COMPLETE;
127                } else {
128                    mPageHandler.addPage(WifiFormPageType.PROXY_SETTINGS);
129                }
130                break;
131            case PROXY_SETTINGS:
132                mProxySettingsPage = formPage;
133                if (mPageHandler.choiceChosen(formPage, R.string.wifi_action_proxy_none)) {
134                    processProxySettings();
135                    if (mSettingsFlow) {
136                        return RESULT_ALL_PAGES_COMPLETE;
137                    } else {
138                        mPageHandler.addPage(WifiFormPageType.IP_SETTINGS);
139                    }
140                } else {
141                    mPageHandler.addPage(WifiFormPageType.PROXY_HOSTNAME);
142                }
143                break;
144            case PROXY_HOSTNAME:
145                mProxyHostnamePage = formPage;
146                mPageHandler.addPage(WifiFormPageType.PROXY_PORT);
147                break;
148            case PROXY_PORT:
149                mProxyPortPage = formPage;
150                mPageHandler.addPage(WifiFormPageType.PROXY_BYPASS);
151                break;
152            case PROXY_BYPASS:
153                mProxyBypassPage = formPage;
154                int proxySettingsResult = processProxySettings();
155                if (proxySettingsResult == 0) {
156                    if (mSettingsFlow) {
157                        return RESULT_ALL_PAGES_COMPLETE;
158                    } else {
159                        mPageHandler.addPage(WifiFormPageType.IP_SETTINGS);
160                    }
161                } else {
162                    mPageHandler.addPage(WifiFormPageType.PROXY_SETTINGS_INVALID);
163                }
164                break;
165            case PROXY_SETTINGS_INVALID:
166                mPageHandler.removePage(mProxySettingsPage);
167                mPageHandler.removePage(mProxyHostnamePage);
168                mPageHandler.removePage(mProxyPortPage);
169                mPageHandler.removePage(mProxyBypassPage);
170                mPageHandler.addPage(WifiFormPageType.PROXY_SETTINGS);
171                break;
172            case IP_SETTINGS:
173                mIpSettingsPage = formPage;
174                if (mPageHandler.choiceChosen(formPage, R.string.wifi_action_dhcp)) {
175                    processIpSettings();
176                    return RESULT_ALL_PAGES_COMPLETE;
177                } else {
178                    mPageHandler.addPage(WifiFormPageType.IP_ADDRESS);
179                }
180                break;
181            case IP_ADDRESS:
182                mIpAddressPage = formPage;
183                mPageHandler.addPage(WifiFormPageType.GATEWAY);
184                break;
185            case GATEWAY:
186                mGatewayPage = formPage;
187                mPageHandler.addPage(WifiFormPageType.NETWORK_PREFIX_LENGTH);
188                break;
189            case NETWORK_PREFIX_LENGTH:
190                mNetworkPrefixLengthPage = formPage;
191                mPageHandler.addPage(WifiFormPageType.DNS1);
192                break;
193            case DNS1:
194                mDns1Page = formPage;
195                mPageHandler.addPage(WifiFormPageType.DNS2);
196                break;
197            case DNS2:
198                mDns2Page = formPage;
199                int ipSettingsResult = processIpSettings();
200                if (ipSettingsResult == 0) {
201                    return RESULT_ALL_PAGES_COMPLETE;
202                } else {
203                    mPageHandler.addPage(WifiFormPageType.IP_SETTINGS_INVALID);
204                }
205                break;
206            case IP_SETTINGS_INVALID:
207                mPageHandler.removePage(mIpSettingsPage);
208                mPageHandler.removePage(mIpAddressPage);
209                mPageHandler.removePage(mGatewayPage);
210                mPageHandler.removePage(mDns1Page);
211                mPageHandler.removePage(mDns2Page);
212                mPageHandler.addPage(WifiFormPageType.IP_SETTINGS);
213                break;
214            default:
215                return RESULT_UNKNOWN_PAGE;
216        }
217        return RESULT_PAGE_HANDLED;
218    }
219
220    public FormPage getPreviousPage(WifiFormPageType formPageType) {
221        switch (formPageType) {
222            case ADVANCED_OPTIONS:
223                return mAdvancedOptionsPage;
224            case PROXY_SETTINGS:
225                if (mProxySettingsPage == null && getInitialProxyInfo() != null) {
226                    return createFormPage(R.string.wifi_action_proxy_manual);
227                }
228                return mProxySettingsPage;
229            case PROXY_HOSTNAME:
230                if (mProxyHostnamePage == null && getInitialProxyInfo() != null) {
231                    return createFormPage(getInitialProxyInfo().getHost());
232                }
233                return mProxyHostnamePage;
234            case PROXY_PORT:
235                if (mProxyPortPage == null && getInitialProxyInfo() != null) {
236                    return createFormPage(Integer.toString(getInitialProxyInfo().getPort()));
237                }
238                return mProxyPortPage;
239            case PROXY_BYPASS:
240                if (mProxyBypassPage == null && getInitialProxyInfo() != null) {
241                    return createFormPage(getInitialProxyInfo().getExclusionListAsString());
242                }
243                return mProxyBypassPage;
244            case IP_SETTINGS:
245                if (mIpSettingsPage == null && getInitialLinkAddress() != null) {
246                    return createFormPage(R.string.wifi_action_static);
247                }
248                return mIpSettingsPage;
249            case IP_ADDRESS:
250                if (mIpAddressPage == null && getInitialLinkAddress() != null) {
251                    return createFormPage(getInitialLinkAddress().getAddress().getHostAddress());
252                }
253                return mIpAddressPage;
254            case GATEWAY:
255                if (mGatewayPage == null && getInitialGateway() != null) {
256                    return createFormPage(getInitialGateway().getHostAddress());
257                }
258                return mGatewayPage;
259            case NETWORK_PREFIX_LENGTH:
260                if (mNetworkPrefixLengthPage == null && getInitialLinkAddress() != null) {
261                    return createFormPage(
262                            Integer.toString(getInitialLinkAddress().getNetworkPrefixLength()));
263                }
264                return mNetworkPrefixLengthPage;
265            case DNS1:
266                if (mDns1Page == null && getInitialDns(0) != null) {
267                    return createFormPage(getInitialDns(0).getHostAddress());
268                }
269                return mDns1Page;
270            case DNS2:
271                if (mDns2Page == null && getInitialDns(1) != null) {
272                    return createFormPage(getInitialDns(1).getHostAddress());
273                }
274                return mDns2Page;
275            case IP_SETTINGS_INVALID:
276            case PROXY_SETTINGS_INVALID:
277            default:
278                return null;
279        }
280    }
281
282    public boolean isEmptyTextAllowed(WifiFormPageType formPageType) {
283        switch (formPageType) {
284            case PROXY_BYPASS:
285            case DNS1:
286            case DNS2:
287            case GATEWAY:
288                return true;
289            default:
290                return false;
291        }
292    }
293
294    private IpConfiguration getCurrentIpConfiguration() {
295        return mIpConfiguration;
296    }
297
298    public void updateConfiguration(WifiConfiguration configuration) {
299        configuration.setIpConfiguration(mIpConfiguration);
300    }
301
302    public void updateConfiguration(NetworkConfiguration configuration) {
303        configuration.setIpConfiguration(mIpConfiguration);
304    }
305
306    private InetAddress getInitialDns(int index) {
307        try {
308            return mInitialConfiguration.getIpConfiguration().getStaticIpConfiguration()
309                    .dnsServers.get(index);
310        } catch (IndexOutOfBoundsException|NullPointerException e) {
311            return null;
312        }
313    }
314
315    private InetAddress getInitialGateway() {
316        try {
317            return mInitialConfiguration.getIpConfiguration().getStaticIpConfiguration().gateway;
318        } catch (NullPointerException e) {
319            return null;
320        }
321    }
322
323    private LinkAddress getInitialLinkAddress() {
324        try {
325            return mInitialConfiguration.getIpConfiguration().getStaticIpConfiguration().ipAddress;
326        } catch (NullPointerException e) {
327            return null;
328        }
329    }
330
331    private ProxyInfo getInitialProxyInfo() {
332        try {
333            return mInitialConfiguration.getIpConfiguration().getHttpProxy();
334        } catch (NullPointerException e) {
335            return null;
336        }
337    }
338
339    private FormPage createFormPage(int resultStringResourceId) {
340        return createFormPage(mContext.getString(resultStringResourceId).toUpperCase());
341    }
342
343    private FormPage createFormPage(String resultString) {
344        Bundle result = new Bundle();
345        result.putString(FormPage.DATA_KEY_SUMMARY_STRING, resultString);
346        FormPage formPage = FormPage.createTextInputForm(resultString);
347        formPage.complete(result);
348        return formPage;
349    }
350
351    private int processProxySettings() {
352        boolean hasProxySettings = (mAdvancedOptionsPage == null || !mPageHandler.choiceChosen(
353                mAdvancedOptionsPage, R.string.wifi_action_advanced_no))
354                && !mPageHandler.choiceChosen(mProxySettingsPage, R.string.wifi_action_proxy_none);
355        mIpConfiguration.setProxySettings(hasProxySettings ?
356                                          ProxySettings.STATIC : ProxySettings.NONE);
357        if (hasProxySettings) {
358            String host = mProxyHostnamePage.getDataSummary();
359            String portStr = mProxyPortPage.getDataSummary();
360            String exclusionList = mProxyBypassPage.getDataSummary();
361            int port = 0;
362            int result = 0;
363            try {
364                port = Integer.parseInt(portStr);
365                result = WifiConfigHelper.validate(host, portStr, exclusionList);
366            } catch (NumberFormatException e) {
367                result = R.string.proxy_error_invalid_port;
368            }
369            if (result == 0) {
370                mIpConfiguration.setHttpProxy(new ProxyInfo(host, port, exclusionList));
371            } else {
372                return result;
373            }
374        } else {
375            mIpConfiguration.setHttpProxy(null);
376        }
377
378        return 0;
379    }
380
381    private int processIpSettings() {
382        boolean hasIpSettings = (mAdvancedOptionsPage == null || !mPageHandler.choiceChosen(
383                mAdvancedOptionsPage, R.string.wifi_action_advanced_no))
384                && !mPageHandler.choiceChosen(mIpSettingsPage, R.string.wifi_action_dhcp);
385        mIpConfiguration.setIpAssignment(hasIpSettings ? IpAssignment.STATIC : IpAssignment.DHCP);
386
387        if (hasIpSettings) {
388            StaticIpConfiguration staticConfig = new StaticIpConfiguration();
389            mIpConfiguration.setStaticIpConfiguration(staticConfig);
390
391            String ipAddr = mIpAddressPage.getDataSummary();
392            if (TextUtils.isEmpty(ipAddr))
393                return R.string.wifi_ip_settings_invalid_ip_address;
394
395            Inet4Address inetAddr = null;
396            try {
397                inetAddr = (Inet4Address) NetworkUtils.numericToInetAddress(ipAddr);
398            } catch (IllegalArgumentException|ClassCastException e) {
399                return R.string.wifi_ip_settings_invalid_ip_address;
400            }
401
402            int networkPrefixLength = -1;
403            try {
404                networkPrefixLength = Integer.parseInt(mNetworkPrefixLengthPage.getDataSummary());
405                if (networkPrefixLength < 0 || networkPrefixLength > 32) {
406                    return R.string.wifi_ip_settings_invalid_network_prefix_length;
407                }
408                staticConfig.ipAddress = new LinkAddress(inetAddr, networkPrefixLength);
409            } catch (NumberFormatException e) {
410                return R.string.wifi_ip_settings_invalid_ip_address;
411            }
412
413            String gateway = mGatewayPage.getDataSummary();
414            if (!TextUtils.isEmpty(gateway)) {
415                try {
416                    staticConfig.gateway =
417                            (Inet4Address) NetworkUtils.numericToInetAddress(gateway);
418                } catch (IllegalArgumentException|ClassCastException e) {
419                    return R.string.wifi_ip_settings_invalid_gateway;
420                }
421            }
422
423            String dns1 = mDns1Page.getDataSummary();
424            if (!TextUtils.isEmpty(dns1)) {
425                try {
426                    staticConfig.dnsServers.add(
427                            (Inet4Address) NetworkUtils.numericToInetAddress(dns1));
428                } catch (IllegalArgumentException|ClassCastException e) {
429                    return R.string.wifi_ip_settings_invalid_dns;
430                }
431            }
432
433            String dns2 = mDns2Page.getDataSummary();
434            if (!TextUtils.isEmpty(dns2)) {
435                try {
436                    staticConfig.dnsServers.add(
437                            (Inet4Address) NetworkUtils.numericToInetAddress(dns1));
438                } catch (IllegalArgumentException|ClassCastException e) {
439                    return R.string.wifi_ip_settings_invalid_dns;
440                }
441            }
442        } else {
443            mIpConfiguration.setStaticIpConfiguration(null);
444        }
445        return 0;
446    }
447}
448