package eu.musesproject.client.contextmonitoring.sensors;
/*
* #%L
* musesclient
* %%
* Copyright (C) 2013 - 2014 HITEC
* %%
* 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.
* #L%
*/
import android.annotation.SuppressLint;
import android.bluetooth.BluetoothAdapter;
import android.content.Context;
import android.net.ConnectivityManager;
import android.net.NetworkInfo;
import android.net.wifi.ScanResult;
import android.net.wifi.WifiInfo;
import android.net.wifi.WifiManager;
import android.os.AsyncTask;
import android.os.Build;
import android.provider.Settings;
import android.util.Log;
import eu.musesproject.client.contextmonitoring.ContextListener;
import eu.musesproject.client.db.entity.SensorConfiguration;
import eu.musesproject.client.model.contextmonitoring.BluetoothState;
import eu.musesproject.contextmodel.ContextEvent;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;
/**
* @author christophstanik
*
* Class to collect information about the connection of
* the device
*/
public class ConnectivitySensor implements ISensor {
private static final String TAG = ConnectivitySensor.class.getSimpleName();
// time in seconds when the sensor polls information
private static final long OBSERVATION_INTERVALL = TimeUnit.SECONDS.toMillis(10);
// sensor identifier
public static final String TYPE = "CONTEXT_SENSOR_CONNECTIVITY";
// context property keys
public static final String PROPERTY_KEY_ID = "id";
public static final String PROPERTY_KEY_MOBILE_CONNECTED = "mobileconnected";
public static final String PROPERTY_KEY_WIFI_ENABLED = "wifienabled";
public static final String PROPERTY_KEY_WIFI_CONNECTED = "wificonnected";
public static final String PROPERTY_KEY_WIFI_NEIGHBORS = "wifineighbors";
public static final String PROPERTY_KEY_HIDDEN_SSID = "hiddenssid";
public static final String PROPERTY_KEY_BSSID = "bssid";
public static final String PROPERTY_KEY_NETWORK_ID = "networkid";
public static final String PROPERTY_KEY_BLUETOOTH_CONNECTED = "bluetoothconnected";
public static final String PROPERTY_KEY_AIRPLANE_MODE = "airplanemode";
public static final String PROPERTY_WIFI_ENCRYPTION = "wifiencryption";
// application context
private Context context;
private ContextListener listener;
// stores all fired context events of this sensor
private List<ContextEvent> contextEventHistory;
// connectivity info API
private ConnectivityManager connectivityManager ;
private WifiManager wifiManager;
private BluetoothAdapter bluetoothAdapter;
// enable and disable sensor
private boolean sensorEnabled;
public ConnectivitySensor(Context context) {
this.context = context;
init();
}
// initializes all necessary default values
private void init() {
sensorEnabled = false;
contextEventHistory = new ArrayList<ContextEvent>(CONTEXT_EVENT_HISTORY_SIZE);
connectivityManager = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
wifiManager = (WifiManager) context.getSystemService(Context.WIFI_SERVICE);
bluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
}
@Override
public void enable() {
if (!sensorEnabled) {
Log.d(TAG, "start connectivity tracking");
sensorEnabled = true;
new ConnectivityObserver().executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
}
}
@Override
public void disable() {
if(sensorEnabled) {
Log.d(TAG, "stop connectivity tracking");
sensorEnabled = false;
}
}
/** adds the context event to the context event history */
private void createContextEvent(ContextEvent contextEvent) {
contextEvent.generateId();
Log.d(TAG, "Connectivity - context event created");
// add context event to the context event history
contextEventHistory.add(contextEvent);
if(contextEventHistory.size() > CONTEXT_EVENT_HISTORY_SIZE) {
contextEventHistory.remove(0);
}
if(listener != null) {
listener.onEvent(contextEvent);
}
}
private boolean identicalContextEvent(ContextEvent oldEvent, ContextEvent newEvent) {
oldEvent.getProperties().remove(PROPERTY_KEY_ID);
if(oldEvent.getProperties().size() != newEvent.getProperties().size()) {
return false;
}
// compare property values
Map<String, String> oldProperties = oldEvent.getProperties();
Map<String, String> newProperties = newEvent.getProperties();
for (Map.Entry<String, String> oldEntry : oldProperties.entrySet()) {
if(!newProperties.containsKey(oldEntry.getKey())) {
return false;
}
else {
if(!newProperties.get(oldEntry.getKey()).equals(oldEntry.getValue())) {
return false;
}
}
}
return true;
}
/**
* Observes the smartphone's connectivity status. Creates a context event whenever a the connectivity status changes.
*/
@SuppressLint("NewApi")
public class ConnectivityObserver extends AsyncTask<Void, Void, Void> {
@SuppressLint("InlinedApi")
@Override
protected Void doInBackground(Void... params) {
while (sensorEnabled) {
NetworkInfo mobileNetworkInfo = connectivityManager.getNetworkInfo(ConnectivityManager.TYPE_MOBILE);
NetworkInfo wifiNetworkInfo = connectivityManager.getNetworkInfo(ConnectivityManager.TYPE_WIFI);
WifiInfo wifiInfo = wifiManager.getConnectionInfo();
// WiFi status
ContextEvent contextEvent = new ContextEvent();
contextEvent.setType(TYPE);
contextEvent.setTimestamp(System.currentTimeMillis());
boolean isMobileConnected;
if (mobileNetworkInfo != null) {
isMobileConnected = mobileNetworkInfo.isConnected();
}
else {
isMobileConnected = false;
}
contextEvent.addProperty(PROPERTY_KEY_MOBILE_CONNECTED, String.valueOf(isMobileConnected));
contextEvent.addProperty(PROPERTY_KEY_WIFI_ENABLED, String.valueOf(wifiManager.isWifiEnabled()));
int wifiNeighbors = -1;
String wifiEncryption = "unknown";
if(wifiManager != null) {
if(wifiManager.isWifiEnabled()) {
List<ScanResult> networkList = wifiManager.getScanResults();
// wifi neighbors
// wifi encryption status
if (networkList != null) {
wifiNeighbors = networkList.size();
for (ScanResult network : networkList) {
if (network.BSSID.equals(wifiInfo.getBSSID())){
wifiEncryption = network.capabilities;
Log.d(TAG, "Connectivity - wifiencryption: " + wifiEncryption);
}
}
}
}
}
contextEvent.addProperty(PROPERTY_KEY_WIFI_NEIGHBORS, String.valueOf(wifiNeighbors));
contextEvent.addProperty(PROPERTY_KEY_WIFI_CONNECTED, String.valueOf(wifiNetworkInfo.isConnected()));
contextEvent.addProperty(PROPERTY_WIFI_ENCRYPTION, wifiEncryption);
contextEvent.addProperty(PROPERTY_KEY_BSSID, String.valueOf(wifiInfo.getBSSID()));
contextEvent.addProperty(PROPERTY_KEY_HIDDEN_SSID, String.valueOf(wifiInfo.getHiddenSSID()));
contextEvent.addProperty(PROPERTY_KEY_NETWORK_ID, String.valueOf(wifiInfo.getNetworkId()));
// bluetooth
// Has to be rewritten
BluetoothState bluetoothState = BluetoothState.FALSE;
if (bluetoothAdapter == null) {
bluetoothState = BluetoothState.NOT_SUPPORTED;
} else {
// check for paired devices to get the connected status
if (bluetoothAdapter.getBondedDevices().size() > 0) {
bluetoothState = BluetoothState.TRUE;
}
}
contextEvent.addProperty(PROPERTY_KEY_BLUETOOTH_CONNECTED,String.valueOf(bluetoothState));
// Airplane mode
boolean airplaneMode;
if(Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN_MR1) {
airplaneMode = Settings.System.getInt(context.getContentResolver(), Settings.System.AIRPLANE_MODE_ON, 0) != 0;
}
else {
airplaneMode = Settings.Global.getInt(context.getContentResolver(), Settings.Global.AIRPLANE_MODE_ON, 0) != 0;
}
contextEvent.addProperty(PROPERTY_KEY_AIRPLANE_MODE, String.valueOf(airplaneMode));
// check if something has changed. If something changed fire a context event, do nothing otherwise.
int connectivityContextListSize = contextEventHistory.size();
if(connectivityContextListSize > 0) {
ContextEvent previousContext = contextEventHistory.get(connectivityContextListSize - 1);
// fire new context event if a connectivity context field changed
if(!identicalContextEvent(previousContext, contextEvent)) {
createContextEvent(contextEvent);
}
}
else {
createContextEvent(contextEvent);
}
try {
Thread.sleep(OBSERVATION_INTERVALL);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
return null;
}
}
@Override
public void addContextListener(ContextListener listener) {
this.listener = listener;
}
@Override
public void removeContextListener(ContextListener listener) {
this.listener = listener;
}
@Override
public ContextEvent getLastFiredContextEvent() {
if(contextEventHistory.size() > 0) {
return contextEventHistory.get(contextEventHistory.size() - 1);
}
else {
return null;
}
}
@Override
public void configure(List<SensorConfiguration> config) {
// TODO Auto-generated method stub
}
@Override
public String getSensorType() {
return TYPE;
}
}