summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--core/java/android/net/ConnectivityManager.java25
-rw-r--r--core/java/android/net/IConnectivityManager.aidl5
-rw-r--r--core/java/android/net/INetworkManagementEventObserver.aidl7
-rw-r--r--core/java/com/android/server/net/BaseNetworkObserver.java5
-rw-r--r--core/res/res/values-zh-rCN/strings.xml3
-rw-r--r--core/res/res/values/bools.xml2
-rw-r--r--core/res/res/values/strings.xml3
-rwxr-xr-xcore/res/res/values/symbols.xml5
-rw-r--r--services/core/java/com/android/server/ConnectivityService.java9
-rw-r--r--services/core/java/com/android/server/NetworkManagementService.java32
-rw-r--r--services/core/java/com/android/server/connectivity/Tethering.java225
-rwxr-xr-xwifi/java/android/net/wifi/WifiDevice.aidl32
-rwxr-xr-xwifi/java/android/net/wifi/WifiDevice.java137
13 files changed, 488 insertions, 2 deletions
diff --git a/core/java/android/net/ConnectivityManager.java b/core/java/android/net/ConnectivityManager.java
index 8e55736..ddfca9e 100644
--- a/core/java/android/net/ConnectivityManager.java
+++ b/core/java/android/net/ConnectivityManager.java
@@ -24,6 +24,7 @@ import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.net.NetworkUtils;
+import android.net.wifi.WifiDevice;
import android.os.Binder;
import android.os.Build.VERSION_CODES;
import android.os.Handler;
@@ -49,6 +50,7 @@ import com.android.internal.util.Protocol;
import java.net.InetAddress;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.HashMap;
+import java.util.List;
import libcore.net.event.NetworkEventDispatcher;
@@ -273,6 +275,15 @@ public class ConnectivityManager {
"android.net.conn.TETHER_STATE_CHANGED";
/**
+ * Broadcast intent action indicating that a Station is connected
+ * or disconnected.
+ *
+ * @hide
+ */
+ public static final String TETHER_CONNECT_STATE_CHANGED =
+ "codeaurora.net.conn.TETHER_CONNECT_STATE_CHANGED";
+
+ /**
* @hide
* gives a String[] listing all the interfaces configured for
* tethering and currently available for tethering.
@@ -1658,6 +1669,20 @@ public class ConnectivityManager {
}
}
+ /**
+ * Get the list of Stations connected to Hotspot.
+ *
+ * @return a list of {@link WifiDevice} objects.
+ * {@hide}
+ */
+ public List<WifiDevice> getTetherConnectedSta() {
+ try {
+ return mService.getTetherConnectedSta();
+ } catch (RemoteException e) {
+ return null;
+ }
+ }
+
/** {@hide} */
public static final int TETHER_ERROR_NO_ERROR = 0;
/** {@hide} */
diff --git a/core/java/android/net/IConnectivityManager.aidl b/core/java/android/net/IConnectivityManager.aidl
index 46c28a6..523dfe1 100644
--- a/core/java/android/net/IConnectivityManager.aidl
+++ b/core/java/android/net/IConnectivityManager.aidl
@@ -26,6 +26,7 @@ import android.net.NetworkQuotaInfo;
import android.net.NetworkRequest;
import android.net.NetworkState;
import android.net.ProxyInfo;
+import android.net.wifi.WifiDevice;
import android.os.IBinder;
import android.os.Messenger;
import android.os.ParcelFileDescriptor;
@@ -36,6 +37,8 @@ import com.android.internal.net.VpnConfig;
import com.android.internal.net.VpnInfo;
import com.android.internal.net.VpnProfile;
+import java.util.List;
+
/**
* Interface that answers queries about, and allows changing, the
* state of network connectivity.
@@ -92,6 +95,8 @@ interface IConnectivityManager
int setUsbTethering(boolean enable);
+ List<WifiDevice> getTetherConnectedSta();
+
void reportInetCondition(int networkType, int percentage);
void reportNetworkConnectivity(in Network network, boolean hasConnectivity);
diff --git a/core/java/android/net/INetworkManagementEventObserver.aidl b/core/java/android/net/INetworkManagementEventObserver.aidl
index b7af374..5a70ff1 100644
--- a/core/java/android/net/INetworkManagementEventObserver.aidl
+++ b/core/java/android/net/INetworkManagementEventObserver.aidl
@@ -92,6 +92,13 @@ interface INetworkManagementEventObserver {
void interfaceClassDataActivityChanged(String label, boolean active, long tsNanos);
/**
+ * Message is received from network interface.
+ *
+ * @param message The message
+ */
+ void interfaceMessageRecevied(String message);
+
+ /**
* Information about available DNS servers has been received.
*
* @param iface The interface on which the information was received.
diff --git a/core/java/com/android/server/net/BaseNetworkObserver.java b/core/java/com/android/server/net/BaseNetworkObserver.java
index 3d9fb5c..c3dcd40 100644
--- a/core/java/com/android/server/net/BaseNetworkObserver.java
+++ b/core/java/com/android/server/net/BaseNetworkObserver.java
@@ -63,6 +63,11 @@ public class BaseNetworkObserver extends INetworkManagementEventObserver.Stub {
}
@Override
+ public void interfaceMessageRecevied(String message) {
+ // default no-op
+ }
+
+ @Override
public void limitReached(String limitName, String iface) {
// default no-op
}
diff --git a/core/res/res/values-zh-rCN/strings.xml b/core/res/res/values-zh-rCN/strings.xml
index 3ffb555..5f51235 100644
--- a/core/res/res/values-zh-rCN/strings.xml
+++ b/core/res/res/values-zh-rCN/strings.xml
@@ -1140,6 +1140,9 @@
<string name="car_mode_disable_notification_message" msgid="8035230537563503262">"触摸可退出车载模式。"</string>
<string name="tethered_notification_title" msgid="3146694234398202601">"网络共享或热点已启用"</string>
<string name="tethered_notification_message" msgid="6857031760103062982">"触摸可进行设置。"</string>
+ <string name="tethered_notification_no_device_message">"没有设备已连接。"</string>
+ <string name="tethered_notification_one_device_message">""<xliff:g id="count">%1$s</xliff:g>"个设备已连接。"</string>
+ <string name="tethered_notification_multi_device_message">""<xliff:g id="count">%1$s</xliff:g>"个设备已连接。"</string>
<string name="back_button_label" msgid="2300470004503343439">"上一步"</string>
<string name="next_button_label" msgid="1080555104677992408">"下一步"</string>
<string name="skip_button_label" msgid="1275362299471631819">"跳过"</string>
diff --git a/core/res/res/values/bools.xml b/core/res/res/values/bools.xml
index 7c63950..4526f92 100644
--- a/core/res/res/values/bools.xml
+++ b/core/res/res/values/bools.xml
@@ -29,4 +29,6 @@
<!-- Whether to allow vertically stacked button bars. This is disabled for
configurations with a small (e.g. less than 320dp) screen height. -->
<bool name="allow_stacked_button_bar">false</bool>
+ <!-- Whether to enable softap extention feature -->
+ <bool name="config_softap_extention">true</bool>
</resources>
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index d9fa287..0b73760 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -3147,6 +3147,9 @@
<!-- Shown when the device is tethered -->
<string name="tethered_notification_title">Tethering or hotspot active</string>
<string name="tethered_notification_message">Touch to set up.</string>
+ <string name="tethered_notification_no_device_message">No connected device</string>
+ <string name="tethered_notification_one_device_message"><xliff:g id="count">%1$s</xliff:g> connected device</string>
+ <string name="tethered_notification_multi_device_message"><xliff:g id="count">%1$s</xliff:g> connected devices</string>
<!-- Strings for possible PreferenceActivity Back/Next buttons -->
<string name="back_button_label">Back</string>
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 7e930c0..d00684f 100755
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -1793,6 +1793,9 @@
<java-symbol type="string" name="smv_application" />
<java-symbol type="string" name="smv_process" />
<java-symbol type="string" name="tethered_notification_message" />
+ <java-symbol type="string" name="tethered_notification_no_device_message" />
+ <java-symbol type="string" name="tethered_notification_one_device_message" />
+ <java-symbol type="string" name="tethered_notification_multi_device_message" />
<java-symbol type="string" name="tethered_notification_title" />
<java-symbol type="string" name="usb_accessory_notification_title" />
<java-symbol type="string" name="usb_mtp_notification_title" />
@@ -2322,4 +2325,6 @@
<java-symbol type="drawable" name="platlogo_m" />
+ <!-- config softap extention feature -->
+ <java-symbol type="bool" name="config_softap_extention" />
</resources>
diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java
index 4919bed..63d8c45 100644
--- a/services/core/java/com/android/server/ConnectivityService.java
+++ b/services/core/java/com/android/server/ConnectivityService.java
@@ -70,6 +70,7 @@ import android.net.ProxyInfo;
import android.net.RouteInfo;
import android.net.UidRange;
import android.net.Uri;
+import android.net.wifi.WifiDevice;
import android.os.Binder;
import android.os.Bundle;
import android.os.FileUtils;
@@ -2515,6 +2516,14 @@ public class ConnectivityService extends IConnectivityManager.Stub
}
}
+ public List<WifiDevice> getTetherConnectedSta() {
+ if (isTetheringSupported()) {
+ return mTethering.getTetherConnectedSta();
+ } else {
+ return null;
+ }
+ }
+
// javadoc from interface
public int tether(String iface) {
ConnectivityManager.enforceTetherChangePermission(mContext);
diff --git a/services/core/java/com/android/server/NetworkManagementService.java b/services/core/java/com/android/server/NetworkManagementService.java
index 433f707..ecfd042 100644
--- a/services/core/java/com/android/server/NetworkManagementService.java
+++ b/services/core/java/com/android/server/NetworkManagementService.java
@@ -170,6 +170,7 @@ public class NetworkManagementService extends INetworkManagementService.Stub
public static final int InterfaceDnsServerInfo = 615;
public static final int RouteChange = 616;
public static final int StrictCleartext = 617;
+ public static final int InterfaceMessage = 618;
}
static final int DAEMON_MSG_MOBILE_CONN_REAL_TIME_INFO = 1;
@@ -520,6 +521,21 @@ public class NetworkManagementService extends INetworkManagementService.Stub
}
/**
+ * Notify our observers of a change in the data activity state of the interface
+ */
+ private void notifyInterfaceMessage(String message) {
+ final int length = mObservers.beginBroadcast();
+ for (int i = 0; i < length; i++) {
+ try {
+ mObservers.getBroadcastItem(i).interfaceMessageRecevied(message);
+ } catch (RemoteException e) {
+ } catch (RuntimeException e) {
+ }
+ }
+ mObservers.finishBroadcast();
+ }
+
+ /**
* Prepare native daemon once connected, enabling modules and pushing any
* existing in-memory rules.
*/
@@ -789,6 +805,22 @@ public class NetworkManagementService extends INetworkManagementService.Stub
}
throw new IllegalStateException(errorMessage);
// break;
+ case NetdResponseCode.InterfaceMessage:
+ /*
+ * An message arrived in network interface.
+ * Format: "NNN IfaceMessage <3>AP-STA-CONNECTED 00:08:22:64:9d:84
+ */
+ if (cooked.length < 3 || !cooked[1].equals("IfaceMessage")) {
+ throw new IllegalStateException(errorMessage);
+ }
+ Slog.d(TAG, "onEvent: "+ raw);
+ if(cooked[4] != null) {
+ notifyInterfaceMessage(cooked[3] + " " + cooked[4]);
+ } else {
+ notifyInterfaceMessage(cooked[3]);
+ }
+ return true;
+ // break;
case NetdResponseCode.InterfaceClassActivity:
/*
* An network interface class state changed (active/idle)
diff --git a/services/core/java/com/android/server/connectivity/Tethering.java b/services/core/java/com/android/server/connectivity/Tethering.java
index c1aaf07..6cacd7b 100644
--- a/services/core/java/com/android/server/connectivity/Tethering.java
+++ b/services/core/java/com/android/server/connectivity/Tethering.java
@@ -36,6 +36,7 @@ import android.net.Network;
import android.net.NetworkInfo;
import android.net.NetworkUtils;
import android.net.RouteInfo;
+import android.net.wifi.WifiDevice;
import android.os.Binder;
import android.os.INetworkManagementService;
import android.os.Looper;
@@ -64,9 +65,22 @@ import java.util.Arrays;
import java.util.Collection;
import java.util.HashMap;
import java.util.Iterator;
+import java.util.List;
import java.util.Set;
import java.util.concurrent.atomic.AtomicInteger;
+import java.io.BufferedReader;
+import java.io.DataInputStream;
+import java.io.File;
+import java.io.FileDescriptor;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.InputStreamReader;
+
+import static android.net.wifi.WifiManager.WIFI_AP_STATE_DISABLED;
+import static android.net.wifi.WifiManager.WIFI_AP_STATE_ENABLED;
+import static android.net.wifi.WifiManager.WIFI_AP_STATE_CHANGED_ACTION;
+
/**
* @hide
@@ -137,6 +151,17 @@ public class Tethering extends BaseNetworkObserver {
private boolean mUsbTetherRequested; // true if USB tethering should be started
// when RNDIS is enabled
+ // Once STA established connection to hostapd, it will be added
+ // to mL2ConnectedDeviceMap. Then after deviceinfo update from dnsmasq,
+ // it will be added to mConnectedDeviceMap
+ private HashMap<String, WifiDevice> mL2ConnectedDeviceMap = new HashMap<String, WifiDevice>();
+ private HashMap<String, WifiDevice> mConnectedDeviceMap = new HashMap<String, WifiDevice>();
+ private static final String dhcpLocation = "/data/misc/dhcp/dnsmasq.leases";
+
+ // Device name polling interval(ms) and max times
+ private static final int DNSMASQ_POLLING_INTERVAL = 1000;
+ private static final int DNSMASQ_POLLING_MAX_TIMES = 10;
+
public Tethering(Context context, INetworkManagementService nmService,
INetworkStatsService statsService, Looper looper) {
mContext = context;
@@ -158,6 +183,8 @@ public class Tethering extends BaseNetworkObserver {
filter.addAction(UsbManager.ACTION_USB_STATE);
filter.addAction(ConnectivityManager.CONNECTIVITY_ACTION);
filter.addAction(Intent.ACTION_CONFIGURATION_CHANGED);
+ filter.addAction(WIFI_AP_STATE_CHANGED_ACTION);
+
mContext.registerReceiver(mStateReceiver, filter);
filter = new IntentFilter();
@@ -326,6 +353,168 @@ public class Tethering extends BaseNetworkObserver {
}
}
+ public List<WifiDevice> getTetherConnectedSta() {
+ Iterator it;
+ List<WifiDevice> TetherConnectedStaList = new ArrayList<WifiDevice>();
+
+ if (mContext.getResources().getBoolean(com.android.internal.R.bool.config_softap_extention)) {
+ it = mConnectedDeviceMap.keySet().iterator();
+ while(it.hasNext()) {
+ String key = (String)it.next();
+ WifiDevice device = (WifiDevice)mConnectedDeviceMap.get(key);
+ if (VDBG) {
+ Log.d(TAG, "getTetherConnectedSta: addr=" + key + " name=" + device.deviceName);
+ }
+ TetherConnectedStaList.add(device);
+ }
+ }
+
+ return TetherConnectedStaList;
+ }
+
+ private void sendTetherConnectStateChangedBroadcast() {
+ if (!getConnectivityManager().isTetheringSupported()) return;
+
+ Intent broadcast = new Intent(ConnectivityManager.TETHER_CONNECT_STATE_CHANGED);
+ broadcast.addFlags(Intent.FLAG_RECEIVER_REPLACE_PENDING |
+ Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
+
+ mContext.sendStickyBroadcastAsUser(broadcast, UserHandle.ALL);
+
+ showTetheredNotification(com.android.internal.R.drawable.stat_sys_tether_wifi);
+ }
+
+ private boolean readDeviceInfoFromDnsmasq(WifiDevice device) {
+ boolean result = false;
+ FileInputStream fstream = null;
+ String line;
+
+ try {
+ fstream = new FileInputStream(dhcpLocation);
+ DataInputStream in = new DataInputStream(fstream);
+ BufferedReader br = new BufferedReader(new InputStreamReader(in));
+
+ while ((null != (line = br.readLine())) && (line.length() != 0)) {
+ String[] fields = line.split(" ");
+
+ // 949295 00:0a:f5:6a:bf:70 192.168.43.32 android-93de88df9ec61bac *
+ if (fields.length > 3) {
+ String addr = fields[1];
+ String name = fields[3];
+
+ if (addr.equals(device.deviceAddress)) {
+ device.deviceName = name;
+ result = true;
+ break;
+ }
+ }
+ }
+ } catch (IOException ex) {
+ Log.e(TAG, "readDeviceNameFromDnsmasq: " + ex);
+ } finally {
+ if (fstream != null) {
+ try {
+ fstream.close();
+ } catch (IOException ex) {}
+ }
+ }
+
+ return result;
+ }
+
+ /*
+ * DnsmasqThread is used to read the Device info from dnsmasq.
+ */
+ private static class DnsmasqThread extends Thread {
+ private final Tethering mTethering;
+ private int mInterval;
+ private int mMaxTimes;
+ private WifiDevice mDevice;
+
+ public DnsmasqThread(Tethering tethering, WifiDevice device,
+ int interval, int maxTimes) {
+ super("Tethering");
+ mTethering = tethering;
+ mInterval = interval;
+ mMaxTimes = maxTimes;
+ mDevice = device;
+ }
+
+ public void run() {
+ boolean result = false;
+
+ try {
+ while (mMaxTimes > 0) {
+ result = mTethering.readDeviceInfoFromDnsmasq(mDevice);
+ if (result) {
+ if (DBG) Log.d(TAG, "Successfully poll device info for " + mDevice.deviceAddress);
+ break;
+ }
+
+ mMaxTimes --;
+ Thread.sleep(mInterval);
+ }
+ } catch (Exception ex) {
+ result = false;
+ Log.e(TAG, "Pulling " + mDevice.deviceAddress + "error" + ex);
+ }
+
+ if (!result) {
+ if (DBG) Log.d(TAG, "Pulling timeout, suppose STA uses static ip " + mDevice.deviceAddress);
+ }
+
+ // When STA uses static ip, device info will be unavaiable from dnsmasq,
+ // thus no matter the result is success or failure, we will broadcast the event.
+ // But if the device is not in L2 connected state, it means the hostapd connection is
+ // disconnected before dnsmasq get device info, so in this case, don't broadcast
+ // connection event.
+ WifiDevice other = mTethering.mL2ConnectedDeviceMap.get(mDevice.deviceAddress);
+ if (other != null && other.deviceState == WifiDevice.CONNECTED) {
+ mTethering.mConnectedDeviceMap.put(mDevice.deviceAddress, mDevice);
+ mTethering.sendTetherConnectStateChangedBroadcast();
+ } else {
+ if (DBG) Log.d(TAG, "Device " + mDevice.deviceAddress + "already disconnected, ignoring");
+ }
+ }
+ }
+
+ public void interfaceMessageRecevied(String message) {
+ // if softap extension feature not enabled, do nothing
+ if (!mContext.getResources().getBoolean(com.android.internal.R.bool.config_softap_extention)) {
+ return;
+ }
+
+ if (DBG) Log.d(TAG, "interfaceMessageRecevied: message=" + message);
+
+ try {
+ WifiDevice device = new WifiDevice(message);
+
+ if (device.deviceState == WifiDevice.CONNECTED) {
+ mL2ConnectedDeviceMap.put(device.deviceAddress, device);
+
+ // When hostapd reported STA-connection event, it is possible that device
+ // info can't fetched from dnsmasq, then we start a thread to poll the
+ // device info, the thread will exit after device info avaiable.
+ // For static ip case, dnsmasq don't hold the device info, thus thread
+ // will exit after a timeout.
+ if (readDeviceInfoFromDnsmasq(device)) {
+ mConnectedDeviceMap.put(device.deviceAddress, device);
+ sendTetherConnectStateChangedBroadcast();
+ } else {
+ if (DBG) Log.d(TAG, "Starting poll device info for " + device.deviceAddress);
+ new DnsmasqThread(this, device,
+ DNSMASQ_POLLING_INTERVAL, DNSMASQ_POLLING_MAX_TIMES).start();
+ }
+ } else if (device.deviceState == WifiDevice.DISCONNECTED) {
+ mL2ConnectedDeviceMap.remove(device.deviceAddress);
+ mConnectedDeviceMap.remove(device.deviceAddress);
+ sendTetherConnectStateChangedBroadcast();
+ }
+ } catch (IllegalArgumentException ex) {
+ Log.e(TAG, "WifiDevice IllegalArgument: " + ex);
+ }
+ }
+
public int tether(String iface) {
if (DBG) Log.d(TAG, "Tethering " + iface);
TetherInterfaceSM sm = null;
@@ -469,8 +658,24 @@ public class Tethering extends BaseNetworkObserver {
Resources r = Resources.getSystem();
CharSequence title = r.getText(com.android.internal.R.string.tethered_notification_title);
- CharSequence message = r.getText(com.android.internal.R.string.
- tethered_notification_message);
+
+ CharSequence message;
+ int size = mConnectedDeviceMap.size();
+
+ if (mContext.getResources().getBoolean(com.android.internal.R.bool.config_softap_extention)
+ && icon == com.android.internal.R.drawable.stat_sys_tether_wifi) {
+ if (size == 0) {
+ message = r.getText(com.android.internal.R.string.tethered_notification_no_device_message);
+ } else if (size == 1) {
+ message = String.format((r.getText(com.android.internal.R.string.tethered_notification_one_device_message)).toString(),
+ size);
+ } else {
+ message = String.format((r.getText(com.android.internal.R.string.tethered_notification_multi_device_message)).toString(),
+ size);
+ }
+ } else {
+ message = r.getText(com.android.internal.R.string.tethered_notification_message);
+ }
if (mTetheredNotificationBuilder == null) {
mTetheredNotificationBuilder = new Notification.Builder(mContext);
@@ -485,10 +690,18 @@ public class Tethering extends BaseNetworkObserver {
.setContentTitle(title)
.setContentText(message)
.setContentIntent(pi);
+ if (mContext.getResources().getBoolean(com.android.internal.R.bool.config_softap_extention)
+ && icon == com.android.internal.R.drawable.stat_sys_tether_wifi
+ && size > 0) {
+ mTetheredNotificationBuilder.setContentText(message);
+ } else {
+ mTetheredNotificationBuilder.setContentTitle(title);
+ }
mLastNotificationId = icon;
notificationManager.notifyAsUser(null, mLastNotificationId,
mTetheredNotificationBuilder.build(), UserHandle.ALL);
+
}
private void clearTetheredNotification() {
@@ -526,6 +739,14 @@ public class Tethering extends BaseNetworkObserver {
}
} else if (action.equals(Intent.ACTION_CONFIGURATION_CHANGED)) {
updateConfiguration();
+ } else if(action.equals(WIFI_AP_STATE_CHANGED_ACTION)){
+ int wifiApState = intent.getIntExtra("wifi_state", WIFI_AP_STATE_DISABLED);
+ if (DBG) Log.d(TAG, "WIFI_AP_STATE_CHANGED: wifiApState=" + wifiApState);
+ if(wifiApState == WIFI_AP_STATE_ENABLED ||
+ wifiApState == WIFI_AP_STATE_DISABLED) {
+ mConnectedDeviceMap.clear();
+ mL2ConnectedDeviceMap.clear();
+ }
}
}
}
diff --git a/wifi/java/android/net/wifi/WifiDevice.aidl b/wifi/java/android/net/wifi/WifiDevice.aidl
new file mode 100755
index 0000000..c1b186c
--- /dev/null
+++ b/wifi/java/android/net/wifi/WifiDevice.aidl
@@ -0,0 +1,32 @@
+/*
+ * Copyright (c) 2014, The Linux Foundation. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials provided
+ * with the distribution.
+ * * Neither the name of The Linux Foundation nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+ * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
+ * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package android.net.wifi;
+
+parcelable WifiDevice;
diff --git a/wifi/java/android/net/wifi/WifiDevice.java b/wifi/java/android/net/wifi/WifiDevice.java
new file mode 100755
index 0000000..163b559
--- /dev/null
+++ b/wifi/java/android/net/wifi/WifiDevice.java
@@ -0,0 +1,137 @@
+/*
+ * Copyright (c) 2014, The Linux Foundation. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials provided
+ * with the distribution.
+ * * Neither the name of The Linux Foundation nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+ * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
+ * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package android.net.wifi;
+
+import android.os.Parcelable;
+import android.os.Parcel;
+
+/**
+ * Describes information about a detected Wi-Fi STA.
+ * {@hide}
+ */
+public class WifiDevice implements Parcelable {
+ /**
+ * The device MAC address is the unique id of a Wi-Fi STA
+ */
+ public String deviceAddress = "";
+
+ /**
+ * The device name is a readable string of a Wi-Fi STA
+ */
+ public String deviceName = "";
+
+ /**
+ * The device state is the state of a Wi-Fi STA
+ */
+ public int deviceState = 0;
+
+ /**
+ * These definitions are for deviceState
+ */
+ public static final int DISCONNECTED = 0;
+ public static final int CONNECTED = 1;
+ public static final int BLACKLISTED = 2;
+
+ private static final String AP_STA_CONNECTED_STR = "AP-STA-CONNECTED";
+ private static final String AP_STA_DISCONNECTED_STR = "AP-STA-DISCONNECTED";
+
+ /** {@hide} */
+ public WifiDevice() {}
+
+ /**
+ * @param string formats supported include
+ *
+ * AP-STA-CONNECTED 42:fc:89:a8:96:09
+ * AP-STA-DISCONNECTED 42:fc:89:a8:96:09
+ *
+ * Note: The events formats can be looked up in the hostapd code
+ * @hide
+ */
+ public WifiDevice(String dataString) throws IllegalArgumentException {
+ String[] tokens = dataString.split(" ");
+
+ if (tokens.length < 2) {
+ throw new IllegalArgumentException();
+ }
+
+ if (tokens[0].indexOf(AP_STA_CONNECTED_STR) != -1) {
+ deviceState = CONNECTED;
+ } else if (tokens[0].indexOf(AP_STA_DISCONNECTED_STR) != -1) {
+ deviceState = DISCONNECTED;
+ } else {
+ throw new IllegalArgumentException();
+ }
+
+ deviceAddress = tokens[1];
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (obj == null || !(obj instanceof WifiDevice)) {
+ return false;
+ }
+
+ WifiDevice other = (WifiDevice) obj;
+
+ if (deviceAddress == null) {
+ return (other.deviceAddress == null);
+ } else {
+ return deviceAddress.equals(other.deviceAddress);
+ }
+ }
+
+ /** Implement the Parcelable interface {@hide} */
+ public int describeContents() {
+ return 0;
+ }
+
+ /** Implement the Parcelable interface {@hide} */
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeString(deviceAddress);
+ dest.writeString(deviceName);
+ dest.writeInt(deviceState);
+ }
+
+ /** Implement the Parcelable interface {@hide} */
+ public static final Creator<WifiDevice> CREATOR =
+ new Creator<WifiDevice>() {
+ public WifiDevice createFromParcel(Parcel in) {
+ WifiDevice device = new WifiDevice();
+ device.deviceAddress = in.readString();
+ device.deviceName = in.readString();
+ device.deviceState = in.readInt();
+ return device;
+ }
+
+ public WifiDevice[] newArray(int size) {
+ return new WifiDevice[size];
+ }
+ };
+}