WifiApConfigStore.java revision 0e8f0d87d62589d9dda54d5a4941d0309e3ecf2a
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.os.Handler;
24import android.os.Message;
25import android.os.Messenger;
26import android.util.Log;
27
28import com.android.internal.util.AsyncChannel;
29import com.android.internal.R;
30import com.android.internal.util.State;
31import com.android.internal.util.StateMachine;
32
33import java.io.BufferedInputStream;
34import java.io.BufferedOutputStream;
35import java.io.DataInputStream;
36import java.io.DataOutputStream;
37import java.io.FileInputStream;
38import java.io.FileOutputStream;
39import java.io.IOException;
40import java.util.UUID;
41
42/**
43 * Provides API to the WifiStateMachine for doing read/write access
44 * to soft access point configuration
45 */
46class WifiApConfigStore extends StateMachine {
47
48    private Context mContext;
49    private static final String TAG = "WifiApConfigStore";
50
51    private static final String AP_CONFIG_FILE = Environment.getDataDirectory() +
52        "/misc/wifi/softap.conf";
53
54    private static final int AP_CONFIG_FILE_VERSION = 2;
55
56    private State mDefaultState = new DefaultState();
57    private State mInactiveState = new InactiveState();
58    private State mActiveState = new ActiveState();
59
60    private WifiConfiguration mWifiApConfig = null;
61    private AsyncChannel mReplyChannel = new AsyncChannel();
62
63    WifiApConfigStore(Context context, Handler target) {
64        super(TAG, target.getLooper());
65
66        mContext = context;
67        addState(mDefaultState);
68            addState(mInactiveState, mDefaultState);
69            addState(mActiveState, mDefaultState);
70
71        setInitialState(mInactiveState);
72    }
73
74    public static WifiApConfigStore makeWifiApConfigStore(Context context, Handler target) {
75        WifiApConfigStore s = new WifiApConfigStore(context, target);
76        s.start();
77        return s;
78    }
79
80    class DefaultState extends State {
81        public boolean processMessage(Message message) {
82            switch (message.what) {
83                case WifiStateMachine.CMD_SET_AP_CONFIG:
84                case WifiStateMachine.CMD_SET_AP_CONFIG_COMPLETED:
85                    Log.e(TAG, "Unexpected message: " + message);
86                    break;
87                case WifiStateMachine.CMD_REQUEST_AP_CONFIG:
88                    mReplyChannel.replyToMessage(message,
89                            WifiStateMachine.CMD_RESPONSE_AP_CONFIG, mWifiApConfig);
90                    break;
91                default:
92                    Log.e(TAG, "Failed to handle " + message);
93                    break;
94            }
95            return HANDLED;
96        }
97    }
98
99    class InactiveState extends State {
100        public boolean processMessage(Message message) {
101            switch (message.what) {
102                case WifiStateMachine.CMD_SET_AP_CONFIG:
103                     WifiConfiguration config = (WifiConfiguration)message.obj;
104                    if (config.SSID != null) {
105                        mWifiApConfig = config;
106                        transitionTo(mActiveState);
107                    } else {
108                        Log.e(TAG, "Try to setup AP config without SSID: " + message);
109                    }
110                    break;
111                default:
112                    return NOT_HANDLED;
113            }
114            return HANDLED;
115        }
116    }
117
118    class ActiveState extends State {
119        public void enter() {
120            new Thread(new Runnable() {
121                public void run() {
122                    writeApConfiguration(mWifiApConfig);
123                    sendMessage(WifiStateMachine.CMD_SET_AP_CONFIG_COMPLETED);
124                }
125            }).start();
126        }
127
128        public boolean processMessage(Message message) {
129            switch (message.what) {
130                //TODO: have feedback to the user when we do this
131                //to indicate the write is currently in progress
132                case WifiStateMachine.CMD_SET_AP_CONFIG:
133                    deferMessage(message);
134                    break;
135                case WifiStateMachine.CMD_SET_AP_CONFIG_COMPLETED:
136                    transitionTo(mInactiveState);
137                    break;
138                default:
139                    return NOT_HANDLED;
140            }
141            return HANDLED;
142        }
143    }
144
145    void loadApConfiguration() {
146        DataInputStream in = null;
147        try {
148            WifiConfiguration config = new WifiConfiguration();
149            in = new DataInputStream(new BufferedInputStream(new FileInputStream(
150                            AP_CONFIG_FILE)));
151
152            int version = in.readInt();
153            if ((version != 1) && (version != 2)) {
154                Log.e(TAG, "Bad version on hotspot configuration file, set defaults");
155                setDefaultApConfiguration();
156                return;
157            }
158            config.SSID = in.readUTF();
159
160            if (version >= 2) {
161                config.apBand = in.readInt();
162                config.apChannel = in.readInt();
163            }
164
165            int authType = in.readInt();
166            config.allowedKeyManagement.set(authType);
167            if (authType != KeyMgmt.NONE) {
168                config.preSharedKey = in.readUTF();
169            }
170            mWifiApConfig = config;
171        } catch (IOException ignore) {
172            setDefaultApConfiguration();
173        } finally {
174            if (in != null) {
175                try {
176                    in.close();
177                } catch (IOException e) {}
178            }
179        }
180    }
181
182    Messenger getMessenger() {
183        return new Messenger(getHandler());
184    }
185
186    private void writeApConfiguration(final WifiConfiguration config) {
187        DataOutputStream out = null;
188        try {
189            out = new DataOutputStream(new BufferedOutputStream(
190                        new FileOutputStream(AP_CONFIG_FILE)));
191
192            out.writeInt(AP_CONFIG_FILE_VERSION);
193            out.writeUTF(config.SSID);
194            out.writeInt(config.apBand);
195            out.writeInt(config.apChannel);
196            int authType = config.getAuthType();
197            out.writeInt(authType);
198            if(authType != KeyMgmt.NONE) {
199                out.writeUTF(config.preSharedKey);
200            }
201        } catch (IOException e) {
202            Log.e(TAG, "Error writing hotspot configuration" + e);
203        } finally {
204            if (out != null) {
205                try {
206                    out.close();
207                } catch (IOException e) {}
208            }
209        }
210    }
211
212    /* Generate a default WPA2 based configuration with a random password.
213       We are changing the Wifi Ap configuration storage from secure settings to a
214       flat file accessible only by the system. A WPA2 based default configuration
215       will keep the device secure after the update */
216    private void setDefaultApConfiguration() {
217        WifiConfiguration config = new WifiConfiguration();
218        config.SSID = mContext.getString(R.string.wifi_tether_configure_ssid_default);
219        config.allowedKeyManagement.set(KeyMgmt.WPA2_PSK);
220        String randomUUID = UUID.randomUUID().toString();
221        //first 12 chars from xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx
222        config.preSharedKey = randomUUID.substring(0, 8) + randomUUID.substring(9,13);
223        sendMessage(WifiStateMachine.CMD_SET_AP_CONFIG, config);
224    }
225}
226