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