diff options
-rw-r--r-- | core/java/android/os/INetworkManagementService.aidl | 11 | ||||
-rw-r--r-- | core/java/android/provider/Settings.java | 7 | ||||
-rw-r--r-- | services/java/com/android/server/NetworkManagementService.java | 19 | ||||
-rw-r--r-- | services/java/com/android/server/WifiService.java | 261 | ||||
-rw-r--r-- | wifi/java/android/net/wifi/IWifiManager.aidl | 10 | ||||
-rw-r--r-- | wifi/java/android/net/wifi/WifiManager.java | 131 |
6 files changed, 434 insertions, 5 deletions
diff --git a/core/java/android/os/INetworkManagementService.aidl b/core/java/android/os/INetworkManagementService.aidl index 92041d8..d1d8b0a 100644 --- a/core/java/android/os/INetworkManagementService.aidl +++ b/core/java/android/os/INetworkManagementService.aidl @@ -162,4 +162,15 @@ interface INetworkManagementService * Check the status of USB RNDIS support */ boolean isUsbRNDISStarted(); + + /** + * Start Wifi Access Point + */ + void startAccessPoint(); + + /** + * Stop Wifi Access Point + */ + void stopAccessPoint(); + } diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java index 18e2647..8f410a9 100644 --- a/core/java/android/provider/Settings.java +++ b/core/java/android/provider/Settings.java @@ -2386,6 +2386,13 @@ public final class Settings { public static final String WIFI_ON = "wifi_on"; /** + * Whether the Wi-Fi AP should be on. + * + * @hide + */ + public static final String WIFI_AP_ON = "wifi_ap_on"; + + /** * The acceptable packet loss percentage (range 0 - 100) before trying * another AP on the same network. */ diff --git a/services/java/com/android/server/NetworkManagementService.java b/services/java/com/android/server/NetworkManagementService.java index 53076de..aa9ced8 100644 --- a/services/java/com/android/server/NetworkManagementService.java +++ b/services/java/com/android/server/NetworkManagementService.java @@ -456,4 +456,23 @@ class NetworkManagementService extends INetworkManagementService.Stub { } throw new IllegalStateException("Got an empty response"); } + + public void startAccessPoint() + throws IllegalStateException { + mContext.enforceCallingOrSelfPermission( + android.Manifest.permission.CHANGE_NETWORK_STATE, "NetworkManagementService"); + mContext.enforceCallingOrSelfPermission( + android.Manifest.permission.CHANGE_WIFI_STATE, "NetworkManagementService"); + mConnector.doCommand(String.format("softap set")); + mConnector.doCommand(String.format("softap start")); + } + + public void stopAccessPoint() throws IllegalStateException { + mContext.enforceCallingOrSelfPermission( + android.Manifest.permission.CHANGE_NETWORK_STATE, "NetworkManagementService"); + mContext.enforceCallingOrSelfPermission( + android.Manifest.permission.CHANGE_WIFI_STATE, "NetworkManagementService"); + mConnector.doCommand("softap stop"); + } + } diff --git a/services/java/com/android/server/WifiService.java b/services/java/com/android/server/WifiService.java index 1455973..7378333 100644 --- a/services/java/com/android/server/WifiService.java +++ b/services/java/com/android/server/WifiService.java @@ -22,6 +22,12 @@ import static android.net.wifi.WifiManager.WIFI_STATE_ENABLED; import static android.net.wifi.WifiManager.WIFI_STATE_ENABLING; import static android.net.wifi.WifiManager.WIFI_STATE_UNKNOWN; +import static android.net.wifi.WifiManager.WIFI_AP_STATE_DISABLED; +import static android.net.wifi.WifiManager.WIFI_AP_STATE_DISABLING; +import static android.net.wifi.WifiManager.WIFI_AP_STATE_ENABLED; +import static android.net.wifi.WifiManager.WIFI_AP_STATE_ENABLING; +import static android.net.wifi.WifiManager.WIFI_AP_STATE_FAILED; + import android.app.AlarmManager; import android.app.PendingIntent; import android.bluetooth.BluetoothA2dp; @@ -40,6 +46,8 @@ import android.net.wifi.WifiStateTracker; import android.net.wifi.ScanResult; import android.net.wifi.WifiConfiguration; import android.net.wifi.SupplicantState; +import android.net.ConnectivityManager; +import android.net.InterfaceConfiguration; import android.net.NetworkStateTracker; import android.net.DhcpInfo; import android.net.NetworkUtils; @@ -47,6 +55,7 @@ import android.os.Binder; import android.os.Handler; import android.os.HandlerThread; import android.os.IBinder; +import android.os.INetworkManagementService; import android.os.Looper; import android.os.Message; import android.os.PowerManager; @@ -67,6 +76,7 @@ import java.util.Set; import java.util.regex.Pattern; import java.io.FileDescriptor; import java.io.PrintWriter; +import java.net.UnknownHostException; import com.android.internal.app.IBatteryStats; import android.backup.IBackupManager; @@ -87,6 +97,7 @@ public class WifiService extends IWifiManager.Stub { private Context mContext; private int mWifiState; + private int mWifiApState; private AlarmManager mAlarmManager; private PendingIntent mIdleIntent; @@ -112,6 +123,10 @@ public class WifiService extends IWifiManager.Stub { private final IBatteryStats mBatteryStats; + private INetworkManagementService nwService; + ConnectivityManager mCm; + private String[] mWifiRegexs; + /** * See {@link Settings.Secure#WIFI_IDLE_MS}. This is the default value if a * Settings.Secure value is not present. This timeout value is chosen as @@ -145,6 +160,9 @@ public class WifiService extends IWifiManager.Stub { private static final int MESSAGE_START_WIFI = 3; private static final int MESSAGE_RELEASE_WAKELOCK = 4; private static final int MESSAGE_UPDATE_STATE = 5; + private static final int MESSAGE_START_ACCESS_POINT = 6; + private static final int MESSAGE_STOP_ACCESS_POINT = 7; + private final WifiHandler mWifiHandler; @@ -180,6 +198,9 @@ public class WifiService extends IWifiManager.Stub { mWifiStateTracker.enableRssiPolling(true); mBatteryStats = BatteryStatsService.getService(); + IBinder b = ServiceManager.getService(Context.NETWORKMANAGEMENT_SERVICE); + nwService = INetworkManagementService.Stub.asInterface(b); + mScanResultCache = new LinkedHashMap<String, ScanResult>( SCAN_RESULT_CACHE_SIZE, 0.75f, true) { /* @@ -196,7 +217,9 @@ public class WifiService extends IWifiManager.Stub { mWifiHandler = new WifiHandler(wifiThread.getLooper()); mWifiState = WIFI_STATE_DISABLED; + mWifiApState = WIFI_AP_STATE_DISABLED; boolean wifiEnabled = getPersistedWifiEnabled(); + boolean wifiAPEnabled = wifiEnabled ? false : getPersistedWifiApEnabled(); mAlarmManager = (AlarmManager)mContext.getSystemService(Context.ALARM_SERVICE); Intent idleIntent = new Intent(ACTION_DEVICE_IDLE, null); @@ -232,7 +255,70 @@ public class WifiService extends IWifiManager.Stub { }, new IntentFilter(Intent.ACTION_AIRPLANE_MODE_CHANGED)); + mContext.registerReceiver( + new BroadcastReceiver() { + @Override + public void onReceive(Context context, Intent intent) { + + ArrayList<String> available = intent.getStringArrayListExtra( + ConnectivityManager.EXTRA_AVAILABLE_TETHER); + ArrayList<String> active = intent.getStringArrayListExtra( + ConnectivityManager.EXTRA_ACTIVE_TETHER); + updateTetherState(available, active); + + } + },new IntentFilter(ConnectivityManager.ACTION_TETHER_STATE_CHANGED)); + setWifiEnabledBlocking(wifiEnabled, false, Process.myUid()); + setWifiApEnabledBlocking(wifiAPEnabled, true, Process.myUid(), null); + } + + private void updateTetherState(ArrayList<String> available, ArrayList<String> tethered) { + + boolean wifiTethered = false; + boolean wifiAvailable = false; + + IBinder b = ServiceManager.getService(Context.NETWORKMANAGEMENT_SERVICE); + INetworkManagementService service = INetworkManagementService.Stub.asInterface(b); + + mCm = (ConnectivityManager)mContext.getSystemService(Context.CONNECTIVITY_SERVICE); + mWifiRegexs = mCm.getTetherableWifiRegexs(); + + for (String intf : available) { + for (String regex : mWifiRegexs) { + if (intf.matches(regex)) { + + InterfaceConfiguration ifcg = null; + try { + ifcg = service.getInterfaceConfig(intf); + if (ifcg != null) { + /* IP/netmask: 169.254.2.1/255.255.255.0 */ + ifcg.ipAddr = (169 << 24) + (254 << 16) + (2 << 8) + 1; + ifcg.netmask = (255 << 24) + (255 << 16) + (255 << 8) + 0; + ifcg.interfaceFlags = "up"; + + service.setInterfaceConfig(intf, ifcg); + } + } catch (Exception e) { + /** + * TODO: Add broadcast to indicate tether failed + */ + Slog.e(TAG, "Error configuring interface " + intf + ", :" + e); + return; + } + + /** + * TODO: Add broadcast to indicate tether failed + */ + if(mCm.tether(intf) == ConnectivityManager.TETHER_ERROR_NO_ERROR) { + Slog.d(TAG, "Tethered "+intf); + } else { + Slog.e(TAG, "Error tethering "+intf); + } + break; + } + } + } } private boolean getPersistedWifiEnabled() { @@ -337,12 +423,16 @@ public class WifiService extends IWifiManager.Stub { * Avoid doing a disable when the current Wifi state is UNKNOWN * TODO: Handle driver load fail and supplicant lost as seperate states */ - if (mWifiState == WIFI_STATE_UNKNOWN && !enable) { + if ((mWifiState == WIFI_STATE_UNKNOWN) && !enable) { return false; } setWifiEnabledState(enable ? WIFI_STATE_ENABLING : WIFI_STATE_DISABLING, uid); + if ((mWifiApState == WIFI_AP_STATE_ENABLED) && enable) { + setWifiApEnabledBlocking(false, true, Process.myUid(), null); + } + if (enable) { synchronized (mWifiStateTracker) { if (!WifiNative.loadDriver()) { @@ -490,6 +580,154 @@ public class WifiService extends IWifiManager.Stub { } } + private boolean getPersistedWifiApEnabled() { + final ContentResolver cr = mContext.getContentResolver(); + try { + return Settings.Secure.getInt(cr, Settings.Secure.WIFI_AP_ON) == 1; + } catch (Settings.SettingNotFoundException e) { + Settings.Secure.putInt(cr, Settings.Secure.WIFI_AP_ON, 0); + return false; + } + } + + private void persistWifiApEnabled(boolean enabled) { + final ContentResolver cr = mContext.getContentResolver(); + Settings.Secure.putInt(cr, Settings.Secure.WIFI_AP_ON, enabled ? 1 : 0); + } + + /** + * see {@link android.net.wifi.WifiManager#startAccessPoint(WifiConfiguration)} + * @param wifiConfig SSID, security and channel details as + * part of WifiConfiguration + * @return {@code true} if the start operation was + * started or is already in the queue. + */ + public boolean setWifiApEnabled(WifiConfiguration wifiConfig, boolean enabled) { + enforceChangePermission(); + if (mWifiHandler == null) return false; + + synchronized (mWifiHandler) { + + long ident = Binder.clearCallingIdentity(); + sWakeLock.acquire(); + Binder.restoreCallingIdentity(ident); + + mLastEnableUid = Binder.getCallingUid(); + + sendAccessPointMessage(enabled, wifiConfig, Binder.getCallingUid()); + } + + return true; + } + + /** + * Enables/disables Wi-Fi AP synchronously. The driver is loaded + * and soft access point configured as a single operation. + * @param enable {@code true} to turn Wi-Fi on, {@code false} to turn it off. + * @param persist {@code true} if the setting should be persisted. + * @param uid The UID of the process making the request. + * @param config The WifiConfiguration for AP + * @return {@code true} if the operation succeeds (or if the existing state + * is the same as the requested state) + */ + /** + * TODO: persist needs to go away in WifiService + * This will affect all persist related functions + * for Access Point + */ + private boolean setWifiApEnabledBlocking(boolean enable, + boolean persist, int uid, WifiConfiguration wifiConfig) { + final int eventualWifiApState = enable ? WIFI_AP_STATE_ENABLED : WIFI_AP_STATE_DISABLED; + + if (mWifiApState == eventualWifiApState) { + return true; + } + + setWifiApEnabledState(enable ? WIFI_AP_STATE_ENABLING : WIFI_AP_STATE_DISABLING, uid); + + if (enable && (mWifiState == WIFI_STATE_ENABLED)) { + setWifiEnabledBlocking(false, true, Process.myUid()); + } + + if (enable) { + synchronized (mWifiStateTracker) { + if (!WifiNative.loadDriver()) { + Slog.e(TAG, "Failed to load Wi-Fi driver for AP mode"); + setWifiApEnabledState(WIFI_AP_STATE_FAILED, uid); + return false; + } + } + + try { + nwService.startAccessPoint(); + } catch(Exception e) { + Slog.e(TAG, "Exception in startAccessPoint()"); + } + + } else { + + try { + nwService.stopAccessPoint(); + } catch(Exception e) { + Slog.e(TAG, "Exception in stopAccessPoint()"); + } + + synchronized (mWifiStateTracker) { + if (!WifiNative.unloadDriver()) { + Slog.e(TAG, "Failed to unload Wi-Fi driver for AP mode"); + setWifiApEnabledState(WIFI_AP_STATE_FAILED, uid); + return false; + } + } + } + + // Success! + if (persist) { + persistWifiApEnabled(enable); + } + setWifiApEnabledState(eventualWifiApState, uid); + return true; + } + + /** + * see {@link WifiManager#getWifiApState()} + * @return One of {@link WifiManager#WIFI_AP_STATE_DISABLED}, + * {@link WifiManager#WIFI_AP_STATE_DISABLING}, + * {@link WifiManager#WIFI_AP_STATE_ENABLED}, + * {@link WifiManager#WIFI_AP_STATE_ENABLING}, + * {@link WifiManager#WIFI_AP_STATE_FAILED} + */ + public int getWifiApEnabledState() { + enforceAccessPermission(); + return mWifiApState; + } + + private void setWifiApEnabledState(int wifiAPState, int uid) { + final int previousWifiApState = mWifiApState; + + long ident = Binder.clearCallingIdentity(); + try { + if (wifiAPState == WIFI_AP_STATE_ENABLED) { + mBatteryStats.noteWifiOn(uid); + } else if (wifiAPState == WIFI_AP_STATE_DISABLED) { + mBatteryStats.noteWifiOff(uid); + } + } catch (RemoteException e) { + } finally { + Binder.restoreCallingIdentity(ident); + } + + // Update state + mWifiApState = wifiAPState; + + // Broadcast + final Intent intent = new Intent(WifiManager.WIFI_AP_STATE_CHANGED_ACTION); + intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT); + intent.putExtra(WifiManager.EXTRA_WIFI_AP_STATE, wifiAPState); + intent.putExtra(WifiManager.EXTRA_PREVIOUS_WIFI_AP_STATE, previousWifiApState); + mContext.sendStickyBroadcast(intent); + } + /** * see {@link android.net.wifi.WifiManager#getConfiguredNetworks()} * @return the list of configured networks @@ -1472,6 +1710,12 @@ public class WifiService extends IWifiManager.Stub { Message.obtain(mWifiHandler, MESSAGE_START_WIFI, scanOnlyMode ? 1 : 0, 0).sendToTarget(); } + private void sendAccessPointMessage(boolean enable, WifiConfiguration wifiConfig, int uid) { + Message.obtain(mWifiHandler, + (enable ? MESSAGE_START_ACCESS_POINT : MESSAGE_STOP_ACCESS_POINT), + 0, uid, wifiConfig).sendToTarget(); + } + private void updateWifiState() { // send a message so it's all serialized Message.obtain(mWifiHandler, MESSAGE_UPDATE_STATE, 0, 0).sendToTarget(); @@ -1606,6 +1850,21 @@ public class WifiService extends IWifiManager.Stub { } } break; + + case MESSAGE_START_ACCESS_POINT: + setWifiApEnabledBlocking(true, + msg.arg1 == 1, + msg.arg2, + (WifiConfiguration) msg.obj); + break; + + case MESSAGE_STOP_ACCESS_POINT: + setWifiApEnabledBlocking(false, + msg.arg1 == 1, + msg.arg2, + (WifiConfiguration) msg.obj); + sWakeLock.release(); + break; } } } diff --git a/wifi/java/android/net/wifi/IWifiManager.aidl b/wifi/java/android/net/wifi/IWifiManager.aidl index f3738e3..d833e33 100644 --- a/wifi/java/android/net/wifi/IWifiManager.aidl +++ b/wifi/java/android/net/wifi/IWifiManager.aidl @@ -29,7 +29,7 @@ import android.net.DhcpInfo; interface IWifiManager { List<WifiConfiguration> getConfiguredNetworks(); - + int addOrUpdateNetwork(in WifiConfiguration config); boolean removeNetwork(int netId); @@ -47,7 +47,7 @@ interface IWifiManager boolean disconnect(); boolean reconnect(); - + boolean reassociate(); WifiInfo getConnectionInfo(); @@ -61,7 +61,7 @@ interface IWifiManager boolean setNumAllowedChannels(int numChannels, boolean persist); int[] getValidChannelCounts(); - + boolean saveConfiguration(); DhcpInfo getDhcpInfo(); @@ -77,5 +77,9 @@ interface IWifiManager void acquireMulticastLock(IBinder binder, String tag); void releaseMulticastLock(); + + boolean setWifiApEnabled(in WifiConfiguration wifiConfig, boolean enable); + + int getWifiApEnabledState(); } diff --git a/wifi/java/android/net/wifi/WifiManager.java b/wifi/java/android/net/wifi/WifiManager.java index 178f76e..9ef8ba1 100644 --- a/wifi/java/android/net/wifi/WifiManager.java +++ b/wifi/java/android/net/wifi/WifiManager.java @@ -123,7 +123,88 @@ public class WifiManager { * @see #getWifiState() */ public static final int WIFI_STATE_UNKNOWN = 4; - + + /** + * Broadcast intent action indicating that Wi-Fi AP has been enabled, disabled, + * enabling, disabling, or failed. + * + * @hide + */ + @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) + public static final String WIFI_AP_STATE_CHANGED_ACTION = + "android.net.wifi.WIFI_AP_STATE_CHANGED"; + + /** + * The lookup key for an int that indicates whether Wi-Fi AP is enabled, + * disabled, enabling, disabling, or failed. Retrieve it with + * {@link android.content.Intent#getIntExtra(String,int)}. + * + * @see #WIFI_AP_STATE_DISABLED + * @see #WIFI_AP_STATE_DISABLING + * @see #WIFI_AP_STATE_ENABLED + * @see #WIFI_AP_STATE_ENABLING + * @see #WIFI_AP_STATE_FAILED + * + * @hide + */ + public static final String EXTRA_WIFI_AP_STATE = "wifi_state"; + /** + * The previous Wi-Fi state. + * + * @see #EXTRA_WIFI_AP_STATE + * + * @hide + */ + public static final String EXTRA_PREVIOUS_WIFI_AP_STATE = "previous_wifi_state"; + /** + * Wi-Fi AP is currently being disabled. The state will change to + * {@link #WIFI_AP_STATE_DISABLED} if it finishes successfully. + * + * @see #WIFI_AP_STATE_CHANGED_ACTION + * @see #getWifiApState() + * + * @hide + */ + public static final int WIFI_AP_STATE_DISABLING = 0; + /** + * Wi-Fi AP is disabled. + * + * @see #WIFI_AP_STATE_CHANGED_ACTION + * @see #getWifiState() + * + * @hide + */ + public static final int WIFI_AP_STATE_DISABLED = 1; + /** + * Wi-Fi AP is currently being enabled. The state will change to + * {@link #WIFI_AP_STATE_ENABLED} if it finishes successfully. + * + * @see #WIFI_AP_STATE_CHANGED_ACTION + * @see #getWifiApState() + * + * @hide + */ + public static final int WIFI_AP_STATE_ENABLING = 2; + /** + * Wi-Fi AP is enabled. + * + * @see #WIFI_AP_STATE_CHANGED_ACTION + * @see #getWifiApState() + * + * @hide + */ + public static final int WIFI_AP_STATE_ENABLED = 3; + /** + * Wi-Fi AP is in a failed state. This state will occur when an error occurs during + * enabling or disabling + * + * @see #WIFI_AP_STATE_CHANGED_ACTION + * @see #getWifiApState() + * + * @hide + */ + public static final int WIFI_AP_STATE_FAILED = 4; + /** * Broadcast intent action indicating that a connection to the supplicant has * been established (and it is now possible @@ -681,6 +762,54 @@ public class WifiManager { } /** + * Start AccessPoint mode with the specified + * configuration. If the radio is already running in + * AP mode, update the new configuration + * Note that starting in access point mode disables station + * mode operation + * @param wifiConfig SSID, security and channel details as + * part of WifiConfiguration + * @return {@code true} if the operation succeeds, {@code false} otherwise + * + * @hide Dont open up yet + */ + public boolean setWifiApEnabled(WifiConfiguration wifiConfig, boolean enabled) { + try { + return mService.setWifiApEnabled(wifiConfig, enabled); + } catch (RemoteException e) { + return false; + } + } + + /** + * Gets the Wi-Fi enabled state. + * @return One of {@link #WIFI_AP_STATE_DISABLED}, + * {@link #WIFI_AP_STATE_DISABLING}, {@link #WIFI_AP_STATE_ENABLED}, + * {@link #WIFI_AP_STATE_ENABLING}, {@link #WIFI_AP_STATE_FAILED} + * @see #isWifiApEnabled() + * + * @hide Dont open yet + */ + public int getWifiApState() { + try { + return mService.getWifiApEnabledState(); + } catch (RemoteException e) { + return WIFI_AP_STATE_FAILED; + } + } + + /** + * Return whether Wi-Fi AP is enabled or disabled. + * @return {@code true} if Wi-Fi AP is enabled + * @see #getWifiApState() + * + * @hide Dont open yet + */ + public boolean isWifiApEnabled() { + return getWifiApState() == WIFI_AP_STATE_ENABLED; + } + + /** * Allows an application to keep the Wi-Fi radio awake. * Normally the Wi-Fi radio may turn off when the user has not used the device in a while. * Acquiring a WifiLock will keep the radio on until the lock is released. Multiple |