WifiApConfigStore.java revision 1e5c1abdeb4e3e180e5344c777f7a7db2095c246
1/*
2 * Copyright (C) 2010 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.server.wifi;
18
19import android.content.Context;
20import android.net.wifi.WifiConfiguration;
21import android.net.wifi.WifiConfiguration.KeyMgmt;
22import android.os.Environment;
23import android.util.Log;
24
25import com.android.internal.R;
26
27import java.io.BufferedInputStream;
28import java.io.BufferedOutputStream;
29import java.io.DataInputStream;
30import java.io.DataOutputStream;
31import java.io.FileInputStream;
32import java.io.FileOutputStream;
33import java.io.IOException;
34import java.util.ArrayList;
35import java.util.Random;
36import java.util.UUID;
37
38/**
39 * Provides API for reading/writing soft access point configuration.
40 */
41public class WifiApConfigStore {
42
43    private static final String TAG = "WifiApConfigStore";
44
45    private static final String DEFAULT_AP_CONFIG_FILE =
46            Environment.getDataDirectory() + "/misc/wifi/softap.conf";
47
48    private static final int AP_CONFIG_FILE_VERSION = 2;
49
50    private static final int RAND_SSID_INT_MIN = 1000;
51    private static final int RAND_SSID_INT_MAX = 9999;
52
53    private WifiConfiguration mWifiApConfig = null;
54
55    private ArrayList<Integer> mAllowed2GChannel = null;
56
57    private final Context mContext;
58    private final String mApConfigFile;
59    private final BackupManagerProxy mBackupManagerProxy;
60
61    WifiApConfigStore(Context context, BackupManagerProxy backupManagerProxy) {
62        this(context, backupManagerProxy, DEFAULT_AP_CONFIG_FILE);
63    }
64
65    WifiApConfigStore(Context context,
66                      BackupManagerProxy backupManagerProxy,
67                      String apConfigFile) {
68        mContext = context;
69        mBackupManagerProxy = backupManagerProxy;
70        mApConfigFile = apConfigFile;
71
72        String ap2GChannelListStr = mContext.getResources().getString(
73                R.string.config_wifi_framework_sap_2G_channel_list);
74        Log.d(TAG, "2G band allowed channels are:" + ap2GChannelListStr);
75
76        if (ap2GChannelListStr != null) {
77            mAllowed2GChannel = new ArrayList<Integer>();
78            String channelList[] = ap2GChannelListStr.split(",");
79            for (String tmp : channelList) {
80                mAllowed2GChannel.add(Integer.parseInt(tmp));
81            }
82        }
83
84        /* Load AP configuration from persistent storage. */
85        mWifiApConfig = loadApConfiguration(mApConfigFile);
86        if (mWifiApConfig == null) {
87            /* Use default configuration. */
88            Log.d(TAG, "Fallback to use default AP configuration");
89            mWifiApConfig = getDefaultApConfiguration();
90
91            /* Save the default configuration to persistent storage. */
92            writeApConfiguration(mApConfigFile, mWifiApConfig);
93        }
94    }
95
96    /**
97     * Return the current soft access point configuration.
98     */
99    public synchronized WifiConfiguration getApConfiguration() {
100        return mWifiApConfig;
101    }
102
103    /**
104     * Update the current soft access point configuration.
105     * Restore to default AP configuration if null is provided.
106     * This can be invoked under context of binder threads (WifiManager.setWifiApConfiguration)
107     * and WifiStateMachine thread (CMD_START_AP).
108     */
109    public synchronized void setApConfiguration(WifiConfiguration config) {
110        if (config == null) {
111            mWifiApConfig = getDefaultApConfiguration();
112        } else {
113            mWifiApConfig = config;
114        }
115        writeApConfiguration(mApConfigFile, mWifiApConfig);
116
117        // Stage the backup of the SettingsProvider package which backs this up
118        mBackupManagerProxy.notifyDataChanged();
119    }
120
121    public ArrayList<Integer> getAllowed2GChannel() {
122        return mAllowed2GChannel;
123    }
124
125    /**
126     * Load AP configuration from persistent storage.
127     */
128    private static WifiConfiguration loadApConfiguration(final String filename) {
129        WifiConfiguration config = null;
130        DataInputStream in = null;
131        try {
132            config = new WifiConfiguration();
133            in = new DataInputStream(
134                    new BufferedInputStream(new FileInputStream(filename)));
135
136            int version = in.readInt();
137            if ((version != 1) && (version != 2)) {
138                Log.e(TAG, "Bad version on hotspot configuration file");
139                return null;
140            }
141            config.SSID = in.readUTF();
142
143            if (version >= 2) {
144                config.apBand = in.readInt();
145                config.apChannel = in.readInt();
146            }
147
148            int authType = in.readInt();
149            config.allowedKeyManagement.set(authType);
150            if (authType != KeyMgmt.NONE) {
151                config.preSharedKey = in.readUTF();
152            }
153        } catch (IOException e) {
154            Log.e(TAG, "Error reading hotspot configuration " + e);
155            config = null;
156        } finally {
157            if (in != null) {
158                try {
159                    in.close();
160                } catch (IOException e) {
161                    Log.e(TAG, "Error closing hotspot configuration during read" + e);
162                }
163            }
164        }
165        return config;
166    }
167
168    /**
169     * Write AP configuration to persistent storage.
170     */
171    private static void writeApConfiguration(final String filename,
172                                             final WifiConfiguration config) {
173        try (DataOutputStream out = new DataOutputStream(new BufferedOutputStream(
174                        new FileOutputStream(filename)))) {
175            out.writeInt(AP_CONFIG_FILE_VERSION);
176            out.writeUTF(config.SSID);
177            out.writeInt(config.apBand);
178            out.writeInt(config.apChannel);
179            int authType = config.getAuthType();
180            out.writeInt(authType);
181            if (authType != KeyMgmt.NONE) {
182                out.writeUTF(config.preSharedKey);
183            }
184        } catch (IOException e) {
185            Log.e(TAG, "Error writing hotspot configuration" + e);
186        }
187    }
188
189    /**
190     * Generate a default WPA2 based configuration with a random password.
191     * We are changing the Wifi Ap configuration storage from secure settings to a
192     * flat file accessible only by the system. A WPA2 based default configuration
193     * will keep the device secure after the update.
194     */
195    private WifiConfiguration getDefaultApConfiguration() {
196        WifiConfiguration config = new WifiConfiguration();
197        config.SSID = mContext.getResources().getString(
198                R.string.wifi_tether_configure_ssid_default) + "_" + getRandomIntForDefaultSsid();
199        config.allowedKeyManagement.set(KeyMgmt.WPA2_PSK);
200        String randomUUID = UUID.randomUUID().toString();
201        //first 12 chars from xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx
202        config.preSharedKey = randomUUID.substring(0, 8) + randomUUID.substring(9, 13);
203        return config;
204    }
205
206    private static int getRandomIntForDefaultSsid() {
207        Random random = new Random();
208        return random.nextInt((RAND_SSID_INT_MAX - RAND_SSID_INT_MIN) + 1) + RAND_SSID_INT_MIN;
209    }
210
211    /**
212     * Generate a temporary WPA2 based configuration for use by the local only hotspot.
213     * This config is not persisted and will not be stored by the WifiApConfigStore.
214     */
215    public static WifiConfiguration generateLocalOnlyHotspotConfig(Context context) {
216        WifiConfiguration config = new WifiConfiguration();
217        config.SSID = context.getResources().getString(
218              R.string.wifi_localhotspot_configure_ssid_default) + "_"
219                      + getRandomIntForDefaultSsid();
220        config.allowedKeyManagement.set(KeyMgmt.WPA2_PSK);
221        String randomUUID = UUID.randomUUID().toString();
222        // first 12 chars from xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx
223        config.preSharedKey = randomUUID.substring(0, 8) + randomUUID.substring(9, 13);
224        return config;
225    }
226}
227