/* * Copyright (C) 2010 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.server.wifi; import android.content.Context; import android.net.wifi.WifiConfiguration; import android.net.wifi.WifiConfiguration.KeyMgmt; import android.os.Environment; import android.os.Handler; import android.os.Message; import android.os.Messenger; import android.util.Log; import com.android.internal.util.AsyncChannel; import com.android.internal.R; import com.android.internal.util.State; import com.android.internal.util.StateMachine; import java.io.BufferedInputStream; import java.io.BufferedOutputStream; import java.io.DataInputStream; import java.io.DataOutputStream; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; import java.util.UUID; /** * Provides API to the WifiStateMachine for doing read/write access * to soft access point configuration */ class WifiApConfigStore extends StateMachine { private Context mContext; private static final String TAG = "WifiApConfigStore"; private static final String AP_CONFIG_FILE = Environment.getDataDirectory() + "/misc/wifi/softap.conf"; private static final int AP_CONFIG_FILE_VERSION = 1; private State mDefaultState = new DefaultState(); private State mInactiveState = new InactiveState(); private State mActiveState = new ActiveState(); private WifiConfiguration mWifiApConfig = null; private AsyncChannel mReplyChannel = new AsyncChannel(); WifiApConfigStore(Context context, Handler target) { super(TAG, target.getLooper()); mContext = context; addState(mDefaultState); addState(mInactiveState, mDefaultState); addState(mActiveState, mDefaultState); setInitialState(mInactiveState); } public static WifiApConfigStore makeWifiApConfigStore(Context context, Handler target) { WifiApConfigStore s = new WifiApConfigStore(context, target); s.start(); return s; } class DefaultState extends State { public boolean processMessage(Message message) { switch (message.what) { case WifiStateMachine.CMD_SET_AP_CONFIG: case WifiStateMachine.CMD_SET_AP_CONFIG_COMPLETED: Log.e(TAG, "Unexpected message: " + message); break; case WifiStateMachine.CMD_REQUEST_AP_CONFIG: mReplyChannel.replyToMessage(message, WifiStateMachine.CMD_RESPONSE_AP_CONFIG, mWifiApConfig); break; default: Log.e(TAG, "Failed to handle " + message); break; } return HANDLED; } } class InactiveState extends State { public boolean processMessage(Message message) { switch (message.what) { case WifiStateMachine.CMD_SET_AP_CONFIG: WifiConfiguration config = (WifiConfiguration) message.obj; if (config.SSID != null) { mWifiApConfig = (WifiConfiguration) message.obj; transitionTo(mActiveState); } else { Log.e(TAG, "Try to setup AP config without SSID: " + message); } break; default: return NOT_HANDLED; } return HANDLED; } } class ActiveState extends State { public void enter() { new Thread(new Runnable() { public void run() { writeApConfiguration(mWifiApConfig); sendMessage(WifiStateMachine.CMD_SET_AP_CONFIG_COMPLETED); } }).start(); } public boolean processMessage(Message message) { switch (message.what) { //TODO: have feedback to the user when we do this //to indicate the write is currently in progress case WifiStateMachine.CMD_SET_AP_CONFIG: deferMessage(message); break; case WifiStateMachine.CMD_SET_AP_CONFIG_COMPLETED: transitionTo(mInactiveState); break; default: return NOT_HANDLED; } return HANDLED; } } void loadApConfiguration() { DataInputStream in = null; try { WifiConfiguration config = new WifiConfiguration(); in = new DataInputStream(new BufferedInputStream(new FileInputStream( AP_CONFIG_FILE))); int version = in.readInt(); if (version != 1) { Log.e(TAG, "Bad version on hotspot configuration file, set defaults"); setDefaultApConfiguration(); return; } config.SSID = in.readUTF(); int authType = in.readInt(); config.allowedKeyManagement.set(authType); if (authType != KeyMgmt.NONE) { config.preSharedKey = in.readUTF(); } mWifiApConfig = config; } catch (IOException ignore) { setDefaultApConfiguration(); } finally { if (in != null) { try { in.close(); } catch (IOException e) {} } } } Messenger getMessenger() { return new Messenger(getHandler()); } private void writeApConfiguration(final WifiConfiguration config) { DataOutputStream out = null; try { out = new DataOutputStream(new BufferedOutputStream( new FileOutputStream(AP_CONFIG_FILE))); out.writeInt(AP_CONFIG_FILE_VERSION); out.writeUTF(config.SSID); int authType = config.getAuthType(); out.writeInt(authType); if(authType != KeyMgmt.NONE) { out.writeUTF(config.preSharedKey); } } catch (IOException e) { Log.e(TAG, "Error writing hotspot configuration" + e); } finally { if (out != null) { try { out.close(); } catch (IOException e) {} } } } /* Generate a default WPA2 based configuration with a random password. We are changing the Wifi Ap configuration storage from secure settings to a flat file accessible only by the system. A WPA2 based default configuration will keep the device secure after the update */ private void setDefaultApConfiguration() { WifiConfiguration config = new WifiConfiguration(); config.SSID = mContext.getString(R.string.wifi_tether_configure_ssid_default); config.allowedKeyManagement.set(KeyMgmt.WPA2_PSK); String randomUUID = UUID.randomUUID().toString(); //first 12 chars from xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx config.preSharedKey = randomUUID.substring(0, 8) + randomUUID.substring(9,13); sendMessage(WifiStateMachine.CMD_SET_AP_CONFIG, config); } }