public class MsgHandler extends Service {

	private static final String TAG = "Sense MsgHandler";
    /**
     * Key for Intent extra that defines the buffer type to send data from. The value should be
     * either {@link #BUFFER_TYPE_FLASH} or {@link #BUFFER_TYPE_MEMORY}.
     */
    public static final String EXTRA_BUFFER_TYPE = "buffer-type";
    public static final int BUFFER_TYPE_FLASH = 1;
    public static final int BUFFER_TYPE_MEMORY = 0;

	private static FileTransmitHandler fileHandler;
	private static DataTransmitHandler dataTransmitHandler;
	private static BufferTransmitHandler bufferHandler;
	private static LocalStorage storage;

	/**
	 * Sends data points for one sensor to CommonSense.
	 * 
	 * @param name
	 *            Sensor name, used to determine the sensor ID at CommonSense
	 * @param description
	 *            Sensor description (previously 'device_type'), used to determine the sensor ID at
	 *            CommonSense
	 * @param dataType
	 *            Sensor data type, used to determine the sensor ID at CommonSense
	 * @param deviceUuid
	 *            (Optional) UUID of the sensor's device. Set null to use this phone as the default
	 *            device.
	 * @param sensorData
	 *            JSON Object with the sensor data.
	 */
	public static void sendSensorData(Context context, String name, String description,
			String dataType, String deviceUuid, JSONObject sensorData) {

		try {
			// get cookie for transmission
			SharedPreferences authPrefs = context.getSharedPreferences(SensePrefs.AUTH_PREFS,
					MODE_PRIVATE);
			String cookie = authPrefs.getString(Auth.LOGIN_COOKIE, null);

			SharedPreferences mainPrefs = context.getSharedPreferences(SensePrefs.MAIN_PREFS,
					MODE_PRIVATE);

			boolean encrypt_credential = mainPrefs.getBoolean(Main.Advanced.ENCRYPT_CREDENTIAL, false);

			if (encrypt_credential) {
				EncryptionHelper decryptor = new EncryptionHelper(context);
                                try {
                                    cookie = decryptor.decrypt(cookie);
                                } catch (EncryptionHelper.EncryptionHelperException e) {
                                    Log.w(TAG, "Error decrypting cookie. Assume data is not encrypted");
                                }
			}

			if (cookie.length() > 0) {

				// prepare message to let handler run task
				Bundle args = new Bundle();
				args.putString("name", name);
				args.putString("description", description);
				args.putString("dataType", dataType);
				args.putString("deviceUuid", deviceUuid);
				args.putString("cookie", cookie);
				Message msg = Message.obtain();
				msg.setData(args);
				msg.obj = sensorData;

				// check for sending a file
				if (dataType.equals(SenseDataTypes.FILE)) {
					fileHandler.sendMessage(msg);
				} else {
					dataTransmitHandler.sendMessage(msg);
				}
			} else {
				Log.w(TAG, "Cannot send data point! no cookie");
			}

		} catch (Exception e) {
			Log.e(TAG, "Error in sending sensor data:", e);
		}
	}

	/**
	 * Puts data from the buffer in the flash database for long-term storage
	 */
	private void emptyBufferToDb() {
		// Log.v(TAG, "Emptying buffer to persistant database...");
		try {
			Uri contentUri = Uri.parse("content://" + getString(R.string.local_storage_authority)
					+ DataPoint.CONTENT_URI_PATH + "?persist=true");
			String where = DataPoint.TRANSMIT_STATE + "=" + 0;
			storage.update(contentUri, new ContentValues(), where, null);
		} catch (IllegalArgumentException e) {
			Log.e(TAG, "Error updating Local Storage!", e);
		}
	}

	private void handleNewMsgIntent(Intent intent) {
		// Log.d(TAG, "handleNewMsgIntent");
		try {
			DecimalFormatSymbols otherSymbols = new DecimalFormatSymbols(Locale.ENGLISH);
			NumberFormat formatter = new DecimalFormat("##########.###", otherSymbols);

			// get data point details from Intent
			String sensorName = intent.getStringExtra(DataPoint.SENSOR_NAME);
			String displayName = intent.getStringExtra(DataPoint.DISPLAY_NAME);
			String description = intent.getStringExtra(DataPoint.SENSOR_DESCRIPTION);
			String dataType = intent.getStringExtra(DataPoint.DATA_TYPE);
			String deviceUuid = intent.getStringExtra(DataPoint.DEVICE_UUID);
			long timestamp = intent.getLongExtra(DataPoint.TIMESTAMP, SNTP.getInstance().getTime());
			String timeInSecs = formatter.format(timestamp / 1000.0d);

			// defaults
			description = description != null ? description : sensorName;
			displayName = displayName != null ? displayName : sensorName;
			deviceUuid = deviceUuid != null ? deviceUuid : SenseApi.getDefaultDeviceUuid(this);

			// convert sensor value to String
			String sensorValue = "";
			if (dataType.equals(SenseDataTypes.BOOL)) {
				sensorValue += intent.getBooleanExtra(DataPoint.VALUE, false);
			} else if (dataType.equals(SenseDataTypes.FLOAT)) {
				sensorValue += intent.getFloatExtra(DataPoint.VALUE, Float.MIN_VALUE);
			} else if (dataType.equals(SenseDataTypes.INT)) {
				sensorValue += intent.getIntExtra(DataPoint.VALUE, Integer.MIN_VALUE);
			} else if (dataType.equals(SenseDataTypes.JSON)
					|| dataType.equals(SenseDataTypes.JSON_TIME_SERIES)) {
                try {
                    sensorValue += new JSONObject(intent.getStringExtra(DataPoint.VALUE))
                            .toString();
                } catch (JSONException e) {
                    // assume value is an array
                    sensorValue += new JSONArray(intent.getStringExtra(DataPoint.VALUE)).toString();
                }
			} else if (dataType.equals(SenseDataTypes.STRING)
					|| dataType.equals(SenseDataTypes.FILE)) {
				sensorValue += intent.getStringExtra(DataPoint.VALUE);
			}

			// put the data point in the local storage
			insertToLocalStorage(sensorName, displayName, description, dataType, deviceUuid,
					timestamp, sensorValue);

			/*
			 * check if we can send the data point immediately
			 */
			SharedPreferences mainPrefs = getSharedPreferences(SensePrefs.MAIN_PREFS, MODE_PRIVATE);
			int rate = Integer.parseInt(mainPrefs.getString(Main.SYNC_RATE, "0"));
			boolean isRealTimeMode = rate == -2;
			if (isOnline() && isRealTimeMode) {

				// create sensor data JSON object with only 1 data point
				JSONObject sensorData = new JSONObject();
				JSONArray dataArray = new JSONArray();
				JSONObject data = new JSONObject();
				data.put("value", sensorValue);
				data.put("date", timeInSecs);
				dataArray.put(data);
				sensorData.put("data", dataArray);
				
				sendSensorData(this, sensorName, description, dataType, deviceUuid, sensorData);
			}

		} catch (Exception e) {
			Log.e(TAG, "Failed to handle new data point!", e);
		}
		
		//broadcast this new data intent as well, so anybody can listen to data!
		this.sendBroadcast(intent);
	}

	private void handleSendIntent(Intent intent) {
		// Log.d(TAG, "handleSendIntent");
		if (isOnline()) {

			// verify the sensor IDs
			startService(new Intent(this, DefaultSensorRegistrationService.class));

			// get the cookie
			SharedPreferences authPrefs = getSharedPreferences(SensePrefs.AUTH_PREFS, MODE_PRIVATE);
			String cookie = authPrefs.getString(Auth.LOGIN_COOKIE, null);

			SharedPreferences mainPrefs = getSharedPreferences(SensePrefs.MAIN_PREFS, MODE_PRIVATE);
			boolean encrypt_credential = mainPrefs.getBoolean(Main.Advanced.ENCRYPT_CREDENTIAL, false);

			if (encrypt_credential) {
				EncryptionHelper decryptor = new EncryptionHelper(this);
                                try {
                                    cookie = decryptor.decrypt(cookie);
                                } catch (EncryptionHelper.EncryptionHelperException e) {
                                    Log.w(TAG, "Error decrypting cookie. Assume data is not encrypted");
                                }
			}


            // send the message to the handler
            Message msg = Message.obtain();
            Bundle args = new Bundle();
            args.putString("cookie", cookie);
            msg.setData(args);
            bufferHandler.sendMessage(msg);
		}
	}

	/**
	 * Inserts a data point as new row in the local storage. Removal of old points is done
	 * automatically.
	 * 
	 * @param name
	 *            Sensor name.
	 * @param displayName
	 *            Sensor display name.
	 * @param description
	 *            Sensor description (previously 'device_type')
	 * @param dataType
	 *            Sensor data type
	 * @param deviceUuid
	 *            (Optional) UUID of the sensor's device. Set null to use the phone as the the
	 *            default device
	 * @param timestamp
	 *            Data point time stamp
	 * @param value
	 *            Data point value
	 */
	private void insertToLocalStorage(String name, String displayName, String description,
			String dataType, String deviceUuid, long timestamp, String value) {

		// new value
		ContentValues values = new ContentValues();
		values.put(DataPoint.SENSOR_NAME, name);
		values.put(DataPoint.DISPLAY_NAME, displayName);
		values.put(DataPoint.SENSOR_DESCRIPTION, description);
		values.put(DataPoint.DATA_TYPE, dataType);
		values.put(DataPoint.DEVICE_UUID, deviceUuid);
		values.put(DataPoint.TIMESTAMP, timestamp);
		values.put(DataPoint.VALUE, value);
		values.put(DataPoint.TRANSMIT_STATE, 0);

		try {
			Uri contentUri = Uri.parse("content://" + getString(R.string.local_storage_authority)
					+ DataPoint.CONTENT_URI_PATH);
			storage.insert(contentUri, values);
		} catch (IllegalArgumentException e) {
			Log.e(TAG, "Error inserting points in Local Storage!", e);
		}
	}

	/**
	 * @return <code>true</code> if the phone has network connectivity.
	 */
	private boolean isOnline() {
		SharedPreferences mainPrefs;
		if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {
			mainPrefs = getSharedPreferences(SensePrefs.MAIN_PREFS, MODE_MULTI_PROCESS);
		} else {
			mainPrefs = getSharedPreferences(SensePrefs.MAIN_PREFS, MODE_PRIVATE);
		}
		boolean isCommonSenseEnabled = mainPrefs.getBoolean(Main.Advanced.USE_COMMONSENSE, true);

		SharedPreferences authPrefs;
		if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {
			authPrefs = getSharedPreferences(SensePrefs.AUTH_PREFS, MODE_MULTI_PROCESS);
		} else {
			authPrefs = getSharedPreferences(SensePrefs.AUTH_PREFS, MODE_PRIVATE);
		}
		boolean isLoggedIn = authPrefs.getString(Auth.LOGIN_COOKIE, null) != null;

		final ConnectivityManager cm = (ConnectivityManager) getSystemService(CONNECTIVITY_SERVICE);
		final NetworkInfo info = cm.getActiveNetworkInfo();
		return (null != info) && info.isConnected() && isCommonSenseEnabled && isLoggedIn;
	}

	@Override
	public IBinder onBind(Intent intent) {
		// you cannot bind to this service
		return null;
	}

	@Override
	public void onCreate() {
		Log.v(TAG, "onCreate");
		super.onCreate();

		storage = LocalStorage.getInstance(this);

		{
			HandlerThread handlerThread = new HandlerThread("TransmitRecentDataThread");
			handlerThread.start();
            bufferHandler = new BufferTransmitHandler(this, storage, handlerThread.getLooper());
		}

		{
			HandlerThread handlerThread = new HandlerThread("TransmitFileThread");
			handlerThread.start();
			fileHandler = new FileTransmitHandler(this, storage, handlerThread.getLooper());
		}

		{
			HandlerThread handlerThread = new HandlerThread("TransmitDataPointThread");
			handlerThread.start();
			dataTransmitHandler = new DataTransmitHandler(this, storage, handlerThread.getLooper());
		}
	}

	@Override
	public void onDestroy() {
		Log.v(TAG, "onDestroy");
		emptyBufferToDb();

		// stop buffered data transmission threads
		bufferHandler.getLooper().quit();
		fileHandler.getLooper().quit();
		dataTransmitHandler.getLooper().quit();

		super.onDestroy();
	}

	/**
	 * Handles an incoming Intent that started the service by checking if it wants to store a new
	 * message or if it wants to send data to CommonSense.
	 */
	@Override
	public int onStartCommand(Intent intent, int flags, int startId) {

		if (getString(R.string.action_sense_new_data).equals(intent.getAction())) {
			handleNewMsgIntent(intent);
		} else if (getString(R.string.action_sense_send_data).equals(intent.getAction())) {
			handleSendIntent(intent);
		} else {
			Log.e(TAG, "Unexpected intent action: " + intent.getAction());
		}

		// this service is not sticky, it will get an intent to restart it if necessary
		return START_NOT_STICKY;
	}
} public class SensePrefs {
    /**
     * Keys for the authentication-related preferences of the Sense Platform
     */
    public static class Auth {
        /**
         * Key for login preference for session cookie.
         * 
         * @see SensePrefs#AUTH_PREFS
         */
        public static final String LOGIN_COOKIE = "login_cookie";
        /**
         *Key for login preference for session id.
         *  
         * @see SensePrefs#AUTH_PREFS
         */
        public static final String LOGIN_SESSION_ID = "session_id";
        /**
         * Key for login preference for email address.
         * 
         * @see SensePrefs#AUTH_PREFS
         */
        public static final String LOGIN_USERNAME = "login_mail";
        /**
         * Key for login preference for hashed password.
         * 
         * @see SensePrefs#AUTH_PREFS
         */
        public static final String LOGIN_PASS = "login_pass";
        /**
         * Key for storing the online sensor list for this device (type of JSONArray).
         * 
         * @see #SENSOR_LIST_COMPLETE
         * @see SensePrefs#AUTH_PREFS
         * @deprecated
         */
        public static final String SENSOR_LIST = "sensor_list";
        /**
         * Key for storing the online sensor list for this user (type of JSONArray).
         * 
         * @see #SENSOR_LIST
         * @see SensePrefs#AUTH_PREFS
         */
        public static final String SENSOR_LIST_COMPLETE = "sensor_list_complete";
        /**
         * Key for storing the retrieval time of device's online sensor list.
         * 
         * @see #SENSOR_LIST_COMPLETE_TIME
         * @see SensePrefs#AUTH_PREFS
         * @deprecated
         */
        public static final String SENSOR_LIST_TIME = "sensor_list_timestamp";
        /**
         * Key for storing the retrieval time of complete online sensor list.
         * 
         * @see #SENSOR_LIST_TIME
         * @see SensePrefs#AUTH_PREFS
         */
        public static final String SENSOR_LIST_COMPLETE_TIME = "sensor_list_complete_timestamp";
        /**
         * Key for storing the online device id.
         * 
         * @see SensePrefs#AUTH_PREFS
         */
        public static final String DEVICE_ID = "device_id";
        /**
         * Key for storing the retrieval time of the online device id.
         * 
         * @see SensePrefs#AUTH_PREFS
         */
        public static final String DEVICE_ID_TIME = "device_id_timestamp";
        /**
         * Key for storing the online device type.
         * 
         * @see SensePrefs#AUTH_PREFS
         */
        public static final String DEVICE_TYPE = "device_type";
        /**
         * Key for storing the IMEI of the phone.
         * 
         * @see SensePrefs#AUTH_PREFS
         */
        public static final String PHONE_IMEI = "phone_imei";
        /**
         * Key for storing the type of the phone.
         * 
         * @see SensePrefs#AUTH_PREFS
         */
        public static final String PHONE_TYPE = "phone_type";
        /**
         * Key for storing if gcm registration_id
         * 
         * @see SensePrefs#AUTH_PREFS
         */
        public static final String GCM_REGISTRATION_ID = "gcm_registration_id";

    }

    /**
     * Keys for the main Sense Platform service preferences
     */
    public static class Main {

        public static class Advanced {
            /**
             * Key to use the development version of CommonSense.
             * 
             * @see SensePrefs#AUTH_PREFS
             */
            public static final String DEV_MODE = "devmode";
            /**
             * Key for preference that toggles use of compression for transmission. Default is true.
             * 
             * @see SensePrefs#MAIN_PREFS
             */
            public static final String COMPRESS = "compression";
            /**
             * Key for preference that enables local storage, making the sensor data available to
             * other apps through a ContentProvider. Default is true.
             * 
             * @see SensePrefs#MAIN_PREFS
             * @deprecated Local storage is always on.
             */
            public static final String LOCAL_STORAGE = "local_storage";
            /**
             * Key for preference that enables communication with CommonSense. Disable this to work
             * in local-only mode. Default is true.
             */
            public static final String USE_COMMONSENSE = "use_commonsense";
            /**
             * Key for preference that enables the location feedback sensor. Enable this to
             * participate in Pim's location feedback test. Default is false.
             */
            public static final String LOCATION_FEEDBACK = "location_feedback";
            /**
             * Key for preference that enables Agostino mode. Enable this to participate in
             * Agostino's saliency test. Default is false.
             */
            public static final String AGOSTINO = "agostino_mode";
            /**
             * Key for preference that enables energy saving mode when using mobile Internet. 
             * When energy saving mode is on data will be uploaded every half an hour.
             * Default is true.
             * @see SensePrefs#MAIN_PREFS
             */            
            public static final String  MOBILE_INTERNET_ENERGY_SAVING_MODE = "mobile_internet_energy_saving_mode";
            /**
             * Key for preference that enables data upload on wifi only.
             * Default is false.
             * @see SensePrefs#MAIN_PREFS
             */            
            public static final String  WIFI_UPLOAD_ONLY = "wifi_upload_only";
            /**
             * Key for value of cache duration for local data in hours.
             * Default is 24.
             * @see SensePrefs#MAIN_PREFS
             */            
            public static final String  RETENTION_HOURS = "retention_hours";
            /**
             * Key for preference that enable database encryption.
             * Default is false.
             * @see SensePrefs#MAIN_PREFS
             */            
            public static final String  ENCRYPT_DATABASE = "encrypt_database";
            /**
             * Key for value of salt for database encryption
             * @see SensePrefs#MAIN_PREFS
             */
            public static final String  ENCRYPT_DATABASE_SALT = "encrypt_database_salt";
            /**
             * Key for preference that enables encryption for credential data.
             * Default is false.
             * @see SensePrefs#MAIN_PREFS
             */            
            public static final String  ENCRYPT_CREDENTIAL = "encrypt_credential";
            /**
             * Key for value of salt for credential encryption.
             * @see SensePrefs#MAIN_PREFS
             */            
            public static final String  ENCRYPT_CREDENTIAL_SALT = "encrypt_credential_salt";
        }
        

        public static class Ambience {
            /**
             * Key for preference that toggles use of light sensor in ambience sensing.
             * 
             * @see SensePrefs#MAIN_PREFS
             */
            public static final String LIGHT = "ambience_light";
            /**
             * Key for preference that toggles use of camera light sensor in ambience sensing.
             * 
             * @see SensePrefs#MAIN_PREFS
             */
            public static final String CAMERA_LIGHT = "ambience_camera_light";
            /**
             * Key for preference that toggles use of the microphone in ambience sensing.
             * 
             * @see SensePrefs#MAIN_PREFS
             */
            public static final String MIC = "ambience_mic";
            /**
             * Key for preference that toggles audio recording ambience sensing.
             * 
             * @see SensePrefs#MAIN_PREFS
             */
            public static final String RECORD_AUDIO = "ambience_record_audio";
            /**
             * Key for preference that toggles use of the audio spectrum in ambience sensing.
             * 
             * @see SensePrefs#MAIN_PREFS
             */
            public static final String AUDIO_SPECTRUM = "ambience_audio_spectrum";
            /**
             * Key for preference that toggles use of the pressure sensor in ambience sensing.
             * 
             * @see SensePrefs#MAIN_PREFS
             */
            public static final String PRESSURE = "ambience_pressure";
            /**
             * Key for preference that toggles use of the temperature sensor in ambience sensing.
             * 
             * @see SensePrefs#MAIN_PREFS
             */
            public static final String TEMPERATURE = "ambience_temperature";
            /**
             * Key for preference that toggles use of the magnetic field sensor in ambience sensing.
             * 
             * @see SensePrefs#MAIN_PREFS
             */
            public static final String MAGNETIC_FIELD = "ambience_magnetic_field";
            /**
             * Key for preference that toggles use of the relative humidity sensor in ambience
             * sensing.
             * 
             * @see SensePrefs#MAIN_PREFS
             */
            public static final String HUMIDITY = "ambience_humidity";
            /**
             * Key for preference that toggles "burst-mode" for the noise sensor
             * 
             * @see SensePrefs#MAIN_PREFS
             */
            public static final String BURSTMODE = "ambience_burstmode";
            /**
             * Key for preference that toggles whether to upload and store burst samples.
             * 
             * @see SensePrefs#MAIN_PREFS
             */
            public static final String DONT_UPLOAD_BURSTS = "ambience_dont_upload_bursts";
            /**
             * Key for preference that toggles whether to record one sided or two sided voice calls 
             * 
             * @see SensePrefs#MAIN_PREFS
             */
            public static final String RECORD_TWO_SIDED_VOICE_CALL = "record_two_sided_voice_call";
        }

        public static class DevProx {
            /**
             * Key for preference that toggles use of Bluetooth in the Device Proximity sensor.
             * 
             * @see SensePrefs#MAIN_PREFS
             */
            public static final String BLUETOOTH = "proximity_bt";
            /**
             * Key for preference that toggles use of Wi-Fi in the Device Proximity sensor.
             * 
             * @see SensePrefs#MAIN_PREFS
             */
            public static final String WIFI = "proximity_wifi";
            /**
             * Key for preference that toggles use of NFC in the Device Proximity sensor.
             * 
             * @see SensePrefs#MAIN_PREFS
             */
            public static final String NFC = "proximity_nfc";
        }

        public static class External {

            public static class MyGlucoHealth {
                /**
                 * Key for preference that toggles use of the MyGlucohealth sensor.
                 * 
                 * @see SensePrefs#MAIN_PREFS
                 */
                public static final String MAIN = "myglucohealth";
            }

            public static class TanitaScale {
                /**
                 * Key for preference that toggles use of the Tanita scale sensor.
                 * 
                 * @see SensePrefs#MAIN_PREFS
                 */
                public static final String MAIN = "tanita_scale";
            }

            public static class ZephyrBioHarness {

                /**
                 * Key for preference that toggles use of the Zephyr BioHarness.
                 * 
                 * @see SensePrefs#MAIN_PREFS
                 */
                public static final String MAIN = "zephyrBioHarness";
                /**
                 * Key for preference that toggles use of the Zephyr BioHarness Accelerometer.
                 * 
                 * @see SensePrefs#MAIN_PREFS
                 */
                public static final String ACC = "zephyrBioHarness_acc";
                /**
                 * Key for preference that toggles use of the Zephyr BioHarness Heart rate.
                 * 
                 * @see SensePrefs#MAIN_PREFS
                 */
                public static final String HEART_RATE = "zephyrBioHarness_heartRate";
                /**
                 * Key for preference that toggles use of the Zephyr BioHarness Temperature.
                 * 
                 * @see SensePrefs#MAIN_PREFS
                 */
                public static final String TEMP = "zephyrBioHarness_temp";
                /**
                 * Key for preference that toggles use of the Zephyr BioHarness Respiration rate.
                 * 
                 * @see SensePrefs#MAIN_PREFS
                 */
                public static final String RESP = "zephyrBioHarness_resp";
                /**
                 * Key for preference that toggles use of the Zephyr BioHarness worn status.
                 * 
                 * @see SensePrefs#MAIN_PREFS
                 */
                public static final String WORN_STATUS = "zephyrBioHarness_wornStatus";
                /**
                 * Key for preference that toggles use of the Zephyr BioHarness battery level.
                 * 
                 * @see SensePrefs#MAIN_PREFS
                 */
                public static final String BATTERY = "zephyrBioHarness_battery";
            }

            public static class ZephyrHxM {
                /**
                 * Key for preference that toggles use of the Zephyr HxM.
                 * 
                 * @see SensePrefs#MAIN_PREFS
                 */
                public static final String MAIN = "zephyrHxM";
                /**
                 * Key for preference that toggles use of the Zephyr HxM speed.
                 * 
                 * @see SensePrefs#MAIN_PREFS
                 */
                public static final String SPEED = "zephyrHxM_speed";
                /**
                 * Key for preference that toggles use of the Zephyr HxM heart rate.
                 * 
                 * @see SensePrefs#MAIN_PREFS
                 */
                public static final String HEART_RATE = "zephyrHxM_heartRate";
                /**
                 * Key for preference that toggles use of the Zephyr HxM battery.
                 * 
                 * @see SensePrefs#MAIN_PREFS
                 */
                public static final String BATTERY = "zephyrHxM_battery";
                /**
                 * Key for preference that toggles use of the Zephyr HxM distance.
                 * 
                 * @see SensePrefs#MAIN_PREFS
                 */
                public static final String DISTANCE = "zephyrHxM_distance";
                /**
                 * Key for preference that toggles use of the Zephyr HxM strides.
                 * 
                 * @see SensePrefs#MAIN_PREFS
                 */
                public static final String STRIDES = "zephyrHxM_strides";
            }

            public static class OBD2Sensor {
                /**
                 * Key for preference that toggles use of the OBD-II sensor.
                 * 
                 * @see SensePrefs#MAIN_PREFS
                 */
                public static final String MAIN = "obd2sensor";
            }
        }

        public static class Location {
            /**
             * Key for preference that toggles use of GPS in location sensor.
             * 
             * @see SensePrefs#MAIN_PREFS
             * @deprecated Selecting the location provider manually will replaced by the fused location provider.
             */
            @Deprecated
            public static final String GPS = "location_gps";
            /**
             * Key for preference that toggles use of Network in location sensor.
             * 
             * @see SensePrefs#MAIN_PREFS
             * @deprecated Selecting the location provider manually will replaced by the fused location provider.
             */
            @Deprecated
            public static final String NETWORK = "location_network";
            /**
             * Key for preference that toggles use of sensor fusion to toggle the GPS usage.
             * 
             * @see SensePrefs#MAIN_PREFS
             * @deprecated Selecting the GPS as provider automatically will be replaced by the fused location provider.
             */
            @Deprecated
            public static final String AUTO_GPS = "automatic_gps";
            /**
             * Key for preference that toggles use of the time zone sensor
             * 
             * @see SensePrefs#MAIN_PREFS
             */
            public static final String TIME_ZONE = "time_zone";
            /**
             * Key for preference that toggles the use of the fused location provider.
             * 
             * @see SensePrefs#MAIN_PREFS
             */
            public static final String FUSED_PROVIDER = "location_fused";
            /**
             * Key for preference that sets the priority for the fused location provider.
             * By default the BALANCED priority is used.
             * 
             * @see SensePrefs#MAIN_PREFS
             */
            public static final String FUSED_PROVIDER_PRIORITY = "location_fused_priority";

            public static class FusedProviderPriority
            {
                /**
                 * Key for preference that toggles the use of the fused location provider with a high power consumption and accuracy.
                 * Accuracy: 1m (most precise location possible)
                 * 
                 * @see SensePrefs#MAIN_PREFS
                 */
                public static final String ACCURATE = "location_fused_accurate";
                /**
                 * Key for preference that toggles the use of the fused location provider with a balance between power consumption and accuracy.
                 * Accuracy: 100 (city block)
                 * 
                 * @see SensePrefs#MAIN_PREFS
                 */
                public static final String BALANCED = "location_fused_balanced";
                /**
                 * Key for preference that toggles the use of the fused location provider with low power consumption and accuracy.
                 * Accuracy: 10km (city level)
                 * 
                 * @see SensePrefs#MAIN_PREFS
                 */
                public static final String LOW_POWER = "location_fused_low_power";
            }
        }

        public static class Motion {
            /**
             * Key for preference that toggles use of Bluetooth in the DeviceProximity sensor.
             * 
             * @see SensePrefs#MAIN_PREFS
             */
            public static final String FALL_DETECT = "motion_fall_detector";
            /**
             * Key for preference that toggles use of Bluetooth in the DeviceProximity sensor.
             * 
             * @see SensePrefs#MAIN_PREFS
             */
            public static final String FALL_DETECT_DEMO = "motion_fall_detector_demo";
            /**
             * Key for preference that toggles "epi-mode", drastically changing motion sensing
             * 
             * @see SensePrefs#MAIN_PREFS
             */
            public static final String EPIMODE = "epimode";
            /**
             * Key for preference that toggles "burst-mode", drastically changing motion sensing
             * 
             * @see SensePrefs#MAIN_PREFS
             */
            public static final String BURSTMODE = "burstmode";
            /**
             * Key for preference that determines whether to unregister the motion sensor between
             * samples. Nota bene: unregistering the sensor breaks the screen rotation on some
             * phones (e.g. Nexus S).
             * 
             * @see SensePrefs#MAIN_PREFS
             */
            
            /**
             * Key for preference that toggles whether to upload and store burst samples.
             * 
             * @see SensePrefs#MAIN_PREFS
             */
            public static final String DONT_UPLOAD_BURSTS = "dont upload bursts";
            
            /**
             * Key for preference that determines the burst duration. Duration is in milliseconds.
             * 
             * @see SensePrefs#MAIN_PREFS
             */
            public static final String BURST_DURATION = "burst_duration";
            
            public static final String UNREG = "motion_unregister";
            /**
             * Key for preference that toggles motion energy sensing, which measures average kinetic
             * energy over a sample period.
             * 
             * @see SensePrefs#MAIN_PREFS
             */
            public static final String MOTION_ENERGY = "motion_energy";
            /**
             * Key for preference that enables fix that re-registers the motion sensor when the
             * screen turns off.
             * 
             * @see SensePrefs#MAIN_PREFS
             */
            public static final String SCREENOFF_FIX = "screenoff_fix";
            /**
             * Key for preference that toggles the use of the gyroscope
             * 
             * @see SensePrefs#MAIN_PREFS
             */
            public static final String GYROSCOPE = "gyroscope";
            /**
             * Key for preference that toggles the use of the accelerometer
             * 
             * @see SensePrefs#MAIN_PREFS
             */
            public static final String ACCELEROMETER = "accelerometer";
            /**
             * Key for preference that toggles the use of the orientation sensor
             * 
             * @see SensePrefs#MAIN_PREFS
             */
            public static final String ORIENTATION = "orientation";
            /**
             * Key for preference that toggles the use of the linear acceleration sensor
             * 
             * @see SensePrefs#MAIN_PREFS
             */
            public static final String LINEAR_ACCELERATION = "linear_acceleration";
        }

        public static class PhoneState {
            /**
             * @see SensePrefs#MAIN_PREFS
             */
            public static final String BATTERY = "phonestate_battery";
            /**
             * @see SensePrefs#MAIN_PREFS
             */
            public static final String SCREEN_ACTIVITY = "phonestate_screen_activity";
            /**
             * @see SensePrefs#MAIN_PREFS
             */
            public static final String PROXIMITY = "phonestate_proximity";
            /**
             * @see SensePrefs#MAIN_PREFS
             */
            public static final String IP_ADDRESS = "phonestate_ip";
            /**
             * @see SensePrefs#MAIN_PREFS
             */
            public static final String DATA_CONNECTION = "phonestate_data_connection";
            /**
             * @see SensePrefs#MAIN_PREFS
             */
            public static final String UNREAD_MSG = "phonestate_unread_msg";
            /**
             * @see SensePrefs#MAIN_PREFS
             */
            public static final String SERVICE_STATE = "phonestate_service_state";
            /**
             * @see SensePrefs#MAIN_PREFS
             */
            public static final String SIGNAL_STRENGTH = "phonestate_signal_strength";
            /**
             * @see SensePrefs#MAIN_PREFS
             */
            public static final String CALL_STATE = "phonestate_call_state";
            /**
             * @see SensePrefs#MAIN_PREFS
             *              */
            public static final String FOREGROUND_APP = "foreground_app";
            /**
             * @see SensePrefs#MAIN_PREFS
             */
            public static final String INSTALLED_APPS = "installed_apps";
            
            /**
             * @see SensePrefs#MAIN_PREFS
             */
			public static final String APP_INFO = "app_info";
        }

        public static class Quiz {
            /**
             * Key for preference that sets the interval between pop quizzes.
             * 
             * @see SensePrefs#MAIN_PREFS
             */
            public static final String RATE = "popquiz_rate";
            /**
             * Key for preference that sets the silent mode for pop quizzes.
             * 
             * @see SensePrefs#MAIN_PREFS
             */
            public static final String SILENT_MODE = "popquiz_silent_mode";
            /**
             * Key for generic preference that starts an update of the quiz questions when clicked.
             * 
             * @see SensePrefs#MAIN_PREFS
             */
            public static final String SYNC = "popquiz_sync";
            /**
             * Key for preference that holds the last update time of the quiz questions with
             * CommonSense.
             * 
             * @see SensePrefs#MAIN_PREFS
             */
            public static final String SYNC_TIME = "popquiz_sync_time";
        }

        public static class SampleRate {
            /**
             * Key for the preference that sets the sample interval to every 15 minutes.
             * 
             * @see SensePrefs#MAIN_PREFS
             */
            public static final String RARELY = "1";

            /**
             * Key for the preference that sets the sample interval to every 3 minutes.
             * For the Location sensors this is 5 minutes.
             * 
             * @see SensePrefs#MAIN_PREFS
             */
            public static final String BALANCED = "2";

            /**
             * Key for the preference that sets the sample interval to every minute.
             * For the Location sensors this is 5 minutes.
             * 
             * @see SensePrefs#MAIN_PREFS
             */
            public static final String NORMAL = "0";

            /**
             * Key for the preference that sets the sample interval to every 10 seconds.
             * For the Location sensors this is every 30 seconds.
             * For the External sensors this is every 5 seconds.
             * For the Motion sensors this is every 5 seconds.
             * 
             * @see SensePrefs#MAIN_PREFS
             */
            public static final String OFTEN = "-1";

            /**
             * Key for the preference that sets the sample interval to every second.
             * For the Ambience sensors this is as fast as possible.
             * For the Noise sensor this enabled the recording of audio files.
             * 
             * @see SensePrefs#MAIN_PREFS
             */
            public static final String REAL_TIME = "-2";
        }

        public static class SyncRate {
            /**
             * Key for the preference that enables data buffering and sets the upload interval to every 15 minutes.
             * 
             * @see SensePrefs#MAIN_PREFS
             */
            public static final String RARELY = "2";
            /**
             * Key for the preference that enables data buffering and sets the upload interval to every 30 minutes.
             * 
             * @see SensePrefs#MAIN_PREFS
             */
            public static final String ECO_MODE = "1";

            /**
             * Key for the preference that enables data buffering and sets the upload interval to every 5 minutes.
             * 
             * @see SensePrefs#MAIN_PREFS
             */
            public static final String NORMAL = "0";

            /**
             * Key for the preference that enables data buffering and sets the upload interval to every minute.
             * 
             * @see SensePrefs#MAIN_PREFS
             */
            public static final String OFTEN = "-1";

            /**
             * Key for the preference that disables data buffering and uploads every data point immediately.
             * 
             * @see SensePrefs#MAIN_PREFS
             */
            public static final String REAL_TIME = "-2";
        }

        /**
         * Key for preference that controls sample frequency of the sensors.
         * 
         * @see SensePrefs#MAIN_PREFS
         */
        public static final String SAMPLE_RATE = "commonsense_rate";
        /**
         * Key for preference that controls sync frequency with CommonSense.
         * 
         * @see SensePrefs#MAIN_PREFS
         */
        public static final String SYNC_RATE = "sync_rate";
        /**
         * Key for preference that saves the last running services.
         * 
         * @see SensePrefs#MAIN_PREFS
         */
        public static final String LAST_STATUS = "last_status";
        /**
         * Key for preference that stores a flag for first login.
         * 
         * @see SensePrefs#MAIN_PREFS
         */
        public static final String LAST_LOGGED_IN = "never_logged_in";
        /**
         * Key for preference that stores a timestamp for last time the sensors registration was
         * verified
         * 
         * @see SensePrefs#MAIN_PREFS
         */
        public static final String LAST_VERIFIED_SENSORS = "verified_sensors";
        /**
         * Key for storing the application key
         * 
         * @see SensePrefs#AUTH_PREFS
         */
        public static final String APPLICATION_KEY = "application_key";
    }

    /**
     * Keys for the status preferences of the Sense Platform service
     */
    public static class Status {
        /**
         * Key for the main status of the sensors. Set to <code>false</code> to disable all the
         * sensing components.
         * 
         * @see SensePrefs#STATUS_PREFS
         */
        public static final String MAIN = "main service status";
        /**
         * Key for the status of the "ambience" sensors. Set to <code>true</code> to enable sensing.
         * 
         * @see SensePrefs#STATUS_PREFS
         */
        public static final String AMBIENCE = "ambience component status";
        /**
         * Key for the status of the "device proximity" sensors. Set to <code>true</code> to enable
         * sensing.
         * 
         * @see SensePrefs#STATUS_PREFS
         */
        public static final String DEV_PROX = "device proximity component status";
        /**
         * Key for the status of the external Bluetooth sensors. Set to <code>true</code> to enable
         * sensing.
         * 
         * @see SensePrefs#STATUS_PREFS
         */
        public static final String EXTERNAL = "external services component status";
        /**
         * Key for the status of the location sensors. Set to <code>true</code> to enable sensing.
         * 
         * @see SensePrefs#STATUS_PREFS
         */
        public static final String LOCATION = "location component status";
        /**
         * Key for the status of the motion sensors. Set to <code>true</code> to enable sensing.
         * 
         * @see SensePrefs#STATUS_PREFS
         */
        public static final String MOTION = "motion component status";
        /**
         * Key for the status of the "phone state" sensors. Set to <code>true</code> to enable
         * sensing.
         * 
         * @see SensePrefs#STATUS_PREFS
         */
        public static final String PHONESTATE = "phone state component status";
        /**
         * Key for the status of the questionnaire. Set to <code>true</code> to enable it.
         * 
         * @see SensePrefs#STATUS_PREFS
         * @deprecated Sense does not support the questionnaire anymore
         */
        public static final String POPQUIZ = "pop quiz component status";
        /**
         * Key for preference to automatically start the Sense service on boot.
         * 
         * @see SensePrefs#STATUS_PREFS
         */
        public static final String AUTOSTART = "autostart";
        /**
        * Key for preference to pause sensing until the next charge.
        * 
        * @see SensePrefs#STATUS_PREFS
        */
        public static final String PAUSED_UNTIL_NEXT_CHARGE = "paused until next charge status";
    }

    public static class SensorSpecifics {
        public static class Loudness {
            /**
             * Key for learned value of total silence..
             */
            public static final String TOTAL_SILENCE = "total_silence";
            /**
             * Key for learned value of highest loudness.
             */
            public static final String LOUDEST = "loudest";
        }

        public static class AutoCalibratedNoise {
            /**
             * Key for learned value of total silence..
             */
            public static final String TOTAL_SILENCE = "AutoCalibratedNoise.total_silence";
            /**
             * Key for learned value of highest loudness.
             */
            public static final String LOUDEST = "AutoCalibratedNoise.loudest";
        }
    }

    /**
     * Name of the shared preferences file used for storing CommonSense authentication data. Use
     * {@link Context#MODE_PRIVATE}.
     * 
     * @see #MAIN_PREFS_PREFS
     * @see #STATUS_PREFS
     */
    public static final String AUTH_PREFS = "authentication";// "login";
    /**
     * Name of the main preference file, used for storing the settings for the Sense service.
     * 
     * @see #AUTH_PREFS
     * @see #STATUS_PREFS
     */
    public static final String MAIN_PREFS = "main";
    /**
     * Name of shared preferences file holding the desired status of the Sense service.
     * 
     * @see #AUTH_PREFS
     * @see #MAIN_PREFS
     */
    public static final String STATUS_PREFS = "service_status_prefs";
    /**
     * Name of the sensor specifics file, used for storing the settings for the Sense service.
     * 
     * @see #AUTH_PREFS
     * @see #STATUS_PREFS
     */
    public static final String SENSOR_SPECIFICS = "sensor_specifics";

    private SensePrefs() {
        // private constructor to prevent instantiation
    }
}
 public static class Ambience {
    /**
     * Key for preference that toggles use of light sensor in ambience sensing.
     * 
     * @see SensePrefs#MAIN_PREFS
     */
    public static final String LIGHT = "ambience_light";
    /**
     * Key for preference that toggles use of camera light sensor in ambience sensing.
     * 
     * @see SensePrefs#MAIN_PREFS
     */
    public static final String CAMERA_LIGHT = "ambience_camera_light";
    /**
     * Key for preference that toggles use of the microphone in ambience sensing.
     * 
     * @see SensePrefs#MAIN_PREFS
     */
    public static final String MIC = "ambience_mic";
    /**
     * Key for preference that toggles audio recording ambience sensing.
     * 
     * @see SensePrefs#MAIN_PREFS
     */
    public static final String RECORD_AUDIO = "ambience_record_audio";
    /**
     * Key for preference that toggles use of the audio spectrum in ambience sensing.
     * 
     * @see SensePrefs#MAIN_PREFS
     */
    public static final String AUDIO_SPECTRUM = "ambience_audio_spectrum";
    /**
     * Key for preference that toggles use of the pressure sensor in ambience sensing.
     * 
     * @see SensePrefs#MAIN_PREFS
     */
    public static final String PRESSURE = "ambience_pressure";
    /**
     * Key for preference that toggles use of the temperature sensor in ambience sensing.
     * 
     * @see SensePrefs#MAIN_PREFS
     */
    public static final String TEMPERATURE = "ambience_temperature";
    /**
     * Key for preference that toggles use of the magnetic field sensor in ambience sensing.
     * 
     * @see SensePrefs#MAIN_PREFS
     */
    public static final String MAGNETIC_FIELD = "ambience_magnetic_field";
    /**
     * Key for preference that toggles use of the relative humidity sensor in ambience
     * sensing.
     * 
     * @see SensePrefs#MAIN_PREFS
     */
    public static final String HUMIDITY = "ambience_humidity";
    /**
     * Key for preference that toggles "burst-mode" for the noise sensor
     * 
     * @see SensePrefs#MAIN_PREFS
     */
    public static final String BURSTMODE = "ambience_burstmode";
    /**
     * Key for preference that toggles whether to upload and store burst samples.
     * 
     * @see SensePrefs#MAIN_PREFS
     */
    public static final String DONT_UPLOAD_BURSTS = "ambience_dont_upload_bursts";
    /**
     * Key for preference that toggles whether to record one sided or two sided voice calls 
     * 
     * @see SensePrefs#MAIN_PREFS
     */
    public static final String RECORD_TWO_SIDED_VOICE_CALL = "record_two_sided_voice_call";
}
 public static class DataPoint implements BaseColumns {

    public static final String CONTENT_TYPE = ContentResolver.CURSOR_DIR_BASE_TYPE
            + "/vnd.sense_os.data_point";
    public static final String CONTENT_ITEM_TYPE = ContentResolver.CURSOR_ITEM_BASE_TYPE
            + "/vnd.sense_os.data_point";
    public static final String CONTENT_URI_PATH = "/recent_values";
    @Deprecated
    public static final String CONTENT_PERSISTED_URI_PATH = "/persisted_values";
    public static final String CONTENT_REMOTE_URI_PATH = "/remote_values";

    /**
     * The name of the sensor that generated the data point. <br>
     * <br>
     * TYPE: String
     */
    public static final String SENSOR_NAME = "sensor_name";
    /**
     * Description of the sensor that generated the data point. Can either be the hardware name,
     * or any other useful description. <br>
     * <br>
     * TYPE: String
     */
    public static final String SENSOR_DESCRIPTION = "sensor_description";
    /**
     * The data type of the data point. <br>
     * <br>
     * TYPE: String
     * 
     * @see SenseDataTypes
     */
    public static final String DATA_TYPE = "data_type";
    /**
     * The human readable display name of the sensor that generated the data point. <br>
     * <br>
     * TYPE: String
     * 
     * @see SenseDataTypes
     */
    public static final String DISPLAY_NAME = "display_name";
    /**
     * Time stamp for the data point, in milliseconds. <br>
     * <br>
     * TYPE: long
     */
    public static final String TIMESTAMP = "timestamp";
    /**
     * Data point value. <br>
     * <br>
     * TYPE: String
     */
    public static final String VALUE = "value";
    /**
     * Transmit state of the data point, signalling whether the point has been sent to
     * CommonSense already.<br>
     * <br>
     * TYPE: integer status code: 0 (not sent), or 1 (sent)
     */
    public static final String TRANSMIT_STATE = "transmit_state";
    /**
     * Device UUID of the sensor. Use this for sensors that are originated from
     * "external sensors", or leave <code>null</code> to use the phone as default device.<br>
     * <br>
     * TYPE: String
     */
    public static final String DEVICE_UUID = "device_uuid";

    private DataPoint() {
        // class should not be instantiated
    }
} public static class SensorNames {

    /**
     * Fall detector sensor. Can be a real fall or a regular free fall for demo's. Part of the
     * Motion sensors.
     */
    public static final String FALL_DETECTOR = "fall_detector";

    /** Noise level sensor. Part of the Ambience sensors. */
    public static final String NOISE = "noise_sensor";

    /** Noise level sensor (Burst-mode). Part of the Ambience sensors. */
    public static final String NOISE_BURST = "noise_sensor (burst-mode)";

    /** Audio spectrum sensor. Part of the Ambience sensors. */
    public static final String AUDIO_SPECTRUM = "audio_spectrum";

    /** Microphone output. Part of the Ambience sensors (real-time mode only). */
    public static final String MIC = "microphone";

    /** Light sensor. Part of the Ambience sensors. */
    public static final String LIGHT = "light";

    /** Camera Light sensor. Part of the Ambience sensors. */
    public static final String CAMERA_LIGHT = "camera_light";

    /** Bluetooth discovery sensor. Part of the Neighboring Devices sensors. */
    public static final String BLUETOOTH_DISCOVERY = "bluetooth_discovery";

    /** Wi-Fi scan sensor. Part of the Neighboring Devices sensors. */
    public static final String WIFI_SCAN = "wifi scan";

    /** NFC sensor. Part of the Neighboring Devices sensors. */
    public static final String NFC_SCAN = "nfc_scan";

    /** Acceleration sensor. Part of the Motion sensors, also used in Zephyr BioHarness */
    public static final String ACCELEROMETER = "accelerometer";

    /** Motion sensor. The basis for the other Motion sensors. */
    public static final String MOTION = "motion";

    /** Linear acceleration sensor name. Part of the Motion sensors. */
    public static final String LIN_ACCELERATION = "linear acceleration";

    /** Gyroscope sensor name. Part of the Motion sensors. */
    public static final String GYRO = "gyroscope";

    /** Magnetic field sensor name. Part of the Ambience sensors. */
    public static final String MAGNETIC_FIELD = "magnetic field";

    /** Orientation sensor name. Part of the Motion sensors. */
    public static final String ORIENT = "orientation";

    /** Epi-mode accelerometer sensor. Special part of the Motion sensors. */
    public static final String ACCELEROMETER_EPI = "accelerometer (epi-mode)";

    /** Burst-mode accelerometer sensor. Special part of the Motion sensors. */
    public static final String ACCELEROMETER_BURST = "accelerometer (burst-mode)";

    /** Burst-mode gyroscope sensor. Special part of the Motion sensors. */
    public static final String GYRO_BURST = "gyroscope (burst-mode)";

    /** Burst-mode linear acceleration sensor. Special part of the Motion sensors. */
    public static final String LINEAR_BURST = "linear acceleration (burst-mode)";

    /** Motion energy sensor name. Special part of the Motion sensors. */
    public static final String MOTION_ENERGY = "motion energy";

    /*** battery sensor for Zephyr BioHarness external sensor */
    public static final String BATTERY_LEVEL = "battery level";

    /** heart rate sensor for Zephyr BioHarness and HxM external sensors */
    public static final String HEART_RATE = "heart rate";

    /** respiration rate sensor for Zephyr BioHarness external sensor */
    public static final String RESPIRATION = "respiration rate";

    /** temperature sensor for Zephyr BioHarness external sensor */
    public static final String TEMPERATURE = "temperature";

    /** worn status for Zephyr BioHarness external sensor */
    public static final String WORN_STATUS = "worn status";

    /** blood pressure sensor */
    public static final String BLOOD_PRESSURE = "blood_pressure";

    /** reaction time sensor */
    public static final String REACTION_TIME = "reaction_time";

    /** speed sensor for Zephyr HxM external sensor */
    public static final String SPEED = "speed";

    /** distance sensor for Zephyr HxM external sensor */
    public static final String DISTANCE = "distance";

    /** battery sensor for Zephyr HxM external sensor */
    public static final String BATTERY_CHARGE = "battery charge";

    /** strides sensor (stappenteller) for Zephyr HxM external sensor */
    public static final String STRIDES = "strides";

    /** Location sensor. */
    public static final String LOCATION = "position";

    /** TimeZone sensor. */
    public static final String TIME_ZONE = "time_zone";

    /** Battery sensor. Part of the Phone State sensors. */
    public static final String BATTERY_SENSOR = "battery sensor";
        
    /** App info sensor. Part of the Phone State sensors. */
    public static final String APP_INFO_SENSOR = "app_info";

    /** Screen activity sensor. Part of the Phone State sensors. */
    public static final String SCREEN_ACTIVITY = "screen activity";

    /** Pressure sensor. Part of the Phone State sensors. */
    public static final String PRESSURE = "pressure";

    /** Proximity sensor. Part of the Phone State sensors. */
    public static final String PROXIMITY = "proximity";

    /** Call state sensor. Part of the Phone State sensors. */
    public static final String CALL_STATE = "call state";

    /** Data connection state sensor. Part of the Phone State sensors. */
    public static final String DATA_CONN = "data connection";

    /** IP address sensor. Part of the Phone State sensors. */
    public static final String IP_ADDRESS = "ip address";

    /** Mobile service state sensor. Part of the Phone State sensors. */
    public static final String SERVICE_STATE = "service state";

    /** Mobile signal strength sensor. Part of the Phone State sensors. */
    public static final String SIGNAL_STRENGTH = "signal strength";

    /** Unread messages sensor. Part of the Phone State sensors. */
    public static final String UNREAD_MSG = "unread msg";

    /** Data connection type sensor. Part of the Phone State sensors. */
    public static final String CONN_TYPE = "connection type";

    /** Monitor status since DTCs cleared. Part of the OBD-II sensors. */
    public static final String MONITOR_STATUS = "monitor status";

    /** Fuel system status. Part of the OBD-II sensors. */
    public static final String FUEL_SYSTEM_STATUS = "fuel system status";

    /** Calculated engine load. Part of the OBD-II sensors. */
    public static final String ENGINE_LOAD = "calculated engine load value";

    /** Engine coolant. Part of the OBD-II sensors. */
    public static final String ENGINE_COOLANT = "engine coolant";

    /** Short/Long term fuel trim bank 1 & 2. Part of the OBD-II sensors. */
    public static final String FUEL_TRIM = "fuel trim";

    /** Fuel Pressure. Part of the OBD-II sensors. */
    public static final String FUEL_PRESSURE = "fuel pressure";

    /** Intake manifold absolute pressure. Part of the OBD-II sensors. */
    public static final String INTAKE_PRESSURE = "intake manifold absolute pressure";

    /** Engine RPM. Part of the OBD-II sensors. */
    public static final String ENGINE_RPM = "engine RPM";

    /** Vehicle speed. Part of the OBD-II sensors. */
    public static final String VEHICLE_SPEED = "vehicle speed";

    /** Timing advance. Part of the OBD-II sensors. */
    public static final String TIMING_ADVANCE = "timing advance";

    /** Intake air temperature. Part of the OBD-II sensors. */
    public static final String INTAKE_TEMPERATURE = "intake air temperature";

    /** MAF air flow rate. Part of the OBD-II sensors. */
    public static final String MAF_AIRFLOW = "MAF air flow rate";

    /** Throttle position. Part of the OBD-II sensors. */
    public static final String THROTTLE_POSITION = "throttle position";

    /** Commanded secondary air status. Part of the OBD-II sensors. */
    public static final String AIR_STATUS = "commanded secondary air status";

    /** Oxygen sensors. Part of the OBD-II sensors. */
    public static final String OXYGEN_SENSORS = "oxygen sensors";

    /** OBD standards. Part of the OBD-II sensors. */
    public static final String OBD_STANDARDS = "OBD standards";

    /** Auxiliary input status. Part of the OBD-II sensors. */
    public static final String AUXILIARY_INPUT = "auxiliary input status";

    /** Run time since engine start. Part of the OBD-II sensors. */
    public static final String RUN_TIME = "run time";

    /** Ambient temperature sensor. Part of the ambience sensors. From API >= 14 only */
    public static final String AMBIENT_TEMPERATURE = "ambient_temperature";

    /** Relative humidity sensor. Part of the ambience sensors. From API >= 14 only */
    public static final String RELATIVE_HUMIDITY = "relative_humidity";

    /** Bluetooth number of neighbours, count sensor */
    public static final String BLUETOOTH_NEIGHBOURS_COUNT = "bluetooth neighbours count";

    /** Traveled distance for each day */
    public static final String TRAVELED_DISTANCE_24H = "traveled distance 24h";

    /** Traveled distance for each hour */
    public static final String TRAVELED_DISTANCE_1H = "traveled distance 1h";

    public static final String LOUDNESS = "loudness";

    public static final String ATTACHED_TO_MYRIANODE = "attachedToMyriaNode";

    public static final String APP_INSTALLED = "installed_apps";

    public static final String APP_FOREGROUND = "foreground_app";

    private SensorNames() {
        // class should not be instantiated
    }
} public abstract class Controller {

    private class Intervals {
        static final long ECO = AlarmManager.INTERVAL_HALF_HOUR;
        static final long RARELY = 1000 * 60 * 15; 
        static final long NORMAL = 1000 * 60 * 5;
        static final long OFTEN = 1000 * 60 * 1;
        static final long BALANCED = 1000 * 60 * 3;
    }

    private static final String TAG = "Controller";
    private static Controller sInstance;

    /**
     * Returns a controller instance
     * 
     * @param context
     *            Context of the Sense service
     * @return the existed controller if any, otherwise the one which has just been created
     */
    public static synchronized Controller getController(Context context) {
        if (sInstance == null) {
            sInstance = new CtrlDefault(context);
        }
        return sInstance;
    }

    private Context mContext;

    protected Controller(Context context) {
        mContext = context;
    }

    /**
     * Retains the mode of Light sensor, and defines it as changed or idle, if the last light
     * measurement had a noticeable difference compared to the previous one or not, respectively
     * 
     * @param value
     *            Last light measurement, in lux.
     */
    public abstract void checkLightSensor(float value);

    /**
     * Retains the mode of Noise sensor, and defines it as loud or idle, if the last noise
     * measurement had a noticeable difference compared to the previous one or not, respectively
     * 
     * @param dB
     *            Last noise measurement, in db.
     */
    public abstract void checkNoiseSensor(double dB);

    /**
     * Checks to see if the sensor is still doing a useful job or whether it is better if we disable
     * it for a while. This method is a callback for a periodic alarm to check the sensor status.
     * 
     * @param isGpsAllowed
     *            True if Gps usage is allowed by the user.
     * @param isListeningNw
     *            True if we currently listen for Network fix.
     * @param isListeningGps
     *            True if we currently listen for Gps fix.
     * @param time
     *            The time between location refresh attempts.
     * @param lastGpsFix
     *            The last fix by Gps provider.
     * @param listenGpsStart
     *            The last time the Gps provider was enabled.
     * @param lastNwFix
     *            The last fix by Network provider.
     * @param listenNwStart
     *            The last time the Network provider was enabled.
     * @param listenGpsStop
     *            The last time the Gps provider was disabled.
     * @param listenNwStop
     *            The last time the Network provider was disabled.
     * 
     * @see #alarmReceiver
     */
    public void checkSensorSettings(boolean isGpsAllowed, boolean isListeningNw,
            boolean isListeningGps, long time, Location lastGpsFix, long listenGpsStart,
            Location lastNwFix, long listenNwStart, long listenGpsStop, long listenNwStop) {
    }
  
    /**
     * Starts periodic transmission of the buffered sensor data.
     */
    public void scheduleTransmissions() {
        Log.v(TAG, "Schedule transmissions");

        SharedPreferences mainPrefs = mContext.getSharedPreferences(SensePrefs.MAIN_PREFS,
                Context.MODE_PRIVATE);
        int syncRate = Integer.parseInt(mainPrefs.getString(Main.SYNC_RATE, SensePrefs.Main.SyncRate.NORMAL));
        int sampleRate = Integer.parseInt(mainPrefs.getString(Main.SAMPLE_RATE, SensePrefs.Main.SampleRate.NORMAL));

        // pick transmission interval
        long txInterval;
        switch (syncRate) {
        case 2: // rarely, every 15 minutes
        	txInterval = Intervals.RARELY;
        	break;
        case 1: // eco-mode
            txInterval = Intervals.ECO;
            break;
        case 0: // 5 minute
            txInterval = Intervals.NORMAL;
            break;
        case -1: // 60 seconds
            txInterval = Intervals.OFTEN;
            break;
        case -2: // real-time: schedule transmission based on sample time
            switch (sampleRate) {
            case 2: // balanced
                txInterval = Intervals.BALANCED * 3;
                break;
            case 1: // rarely
                txInterval = Intervals.ECO * 3;
                break;
            case 0: // normal
                txInterval = Intervals.NORMAL * 3;
                break;
            case -1: // often
                txInterval = Intervals.OFTEN * 3;
                break;
            case -2: // real time
                txInterval = Intervals.OFTEN;
                break;
            default:
                Log.w(TAG, "Unexpected sample rate value: " + sampleRate);
                return;
            }
            break;
        default:
            Log.w(TAG, "Unexpected sync rate value: " + syncRate);
            return;
        }

        // pick transmitter task interval
        long txTaskInterval;
        switch (sampleRate) {
        case -2: // real time
            txTaskInterval = 0;
            break;
        case -1: // often
            txTaskInterval = 10 * 1000;
            break;
        case 0: // normal
            txTaskInterval = 60 * 1000;
            break;
        case 1: // rarely (15 minutes)
            txTaskInterval = 15 * 60 * 1000;
            break;
        case 2: // balanced (3 minutes)
            txTaskInterval = 3 * 60 * 1000;
            break;
        default:
            Log.w(TAG, "Unexpected sample rate value: " + sampleRate);
            return;
        }

        DataTransmitter transmitter = DataTransmitter.getInstance(mContext);
        transmitter.startTransmissions(txInterval, txTaskInterval);
    }
} public class SNTP {
    private static final String TAG = "SNTP";

    private static final int ORIGINATE_TIME_OFFSET = 24;
    private static final int RECEIVE_TIME_OFFSET = 32;
    private static final int TRANSMIT_TIME_OFFSET = 40;
    private static final int NTP_PACKET_SIZE = 48;

    private static final int NTP_PORT = 123;
    private static final int NTP_MODE_CLIENT = 3;
    private static final int NTP_VERSION = 3;
    public static final String HOST_WORLDWIDE = "pool.ntp.org";
    public static final String HOST_ASIA = "asia.pool.ntp.org";
    public static final String HOST_EUROPE = "europe.pool.ntp.org";
    public static final String HOST_NORTH_AMERICA = "north-america.pool.ntp.org";
    public static final String HOST_SOUTH_AMERICA = "south-america.pool.ntp.org";
    public static final String HOST_OCEANIA = "oceania.pool.ntp.org";
    private boolean useSimulateTime = false;

    // Number of seconds between Jan 1, 1900 and Jan 1, 1970
    // 70 years plus 17 leap days
    private static final long OFFSET_1900_TO_1970 = ((365L * 70L) + 17L) * 24L * 60L * 60L;

    // system time computed from NTP server response
    private long mNtpTime;

    // value of SystemClock.elapsedRealtime() corresponding to mNtpTime
    private long mNtpTimeReference;

    // round trip time in milliseconds
    private long mRoundTripTime;

    private long clockOffset = -1;

    private static SNTP sntp = null;

    /**
     * Returns whether time simulation is used
     * @return True is simulate time is being used
     */
    public boolean isSimulatingTime()
    {
        return useSimulateTime;
    }

    /**
     * Sets the current simulation time
     *
     * Will set the clock to the provided simulation time.<br>
     * #getTime will give the updated time based on this simulation time<br>
     * @param time The epoch time in ms to use as current time
     */
    public void setCurrentSimulationTime(long time)
    {
        clockOffset = time - System.currentTimeMillis();
        useSimulateTime = true;
    }

    /**
     * Disables the time simulation
     */
    public void disableTimeSimulation()
    {
        if(!useSimulateTime)
            return;
        clockOffset = -1;
        useSimulateTime = false;
    }
    /*
     * Nice singleton :)
     */
    public synchronized static SNTP getInstance() {
        if (sntp == null)
            sntp = new SNTP();
        return sntp;
    }

    /**
     * Sends an SNTP request to the given host and processes the response.
     * 
     * @param host
     *            host name of the server.
     * @param timeout
     *            network timeout in milliseconds.
     * @return true if the transaction was successful.
     */
    public boolean requestTime(String host, int timeout) {
	try {
	    DatagramSocket socket = new DatagramSocket();
	    socket.setSoTimeout(timeout);
	    InetAddress address = InetAddress.getByName(host);
	    byte[] buffer = new byte[NTP_PACKET_SIZE];
	    DatagramPacket request = new DatagramPacket(buffer, buffer.length, address, NTP_PORT);

	    // set mode = 3 (client) and version = 3
	    // mode is in low 3 bits of first byte
	    // version is in bits 3-5 of first byte
	    buffer[0] = NTP_MODE_CLIENT | (NTP_VERSION << 3);

	    // get current time and write it to the request packet
	    long requestTime = System.currentTimeMillis();
	    long requestTicks = SystemClock.elapsedRealtime();
	    writeTimeStamp(buffer, TRANSMIT_TIME_OFFSET, requestTime);

	    socket.send(request);

	    // read the response
	    DatagramPacket response = new DatagramPacket(buffer, buffer.length);
	    socket.receive(response);
	    long responseTicks = SystemClock.elapsedRealtime();
	    long responseTime = requestTime + (responseTicks - requestTicks);
	    socket.close();

	    // extract the results
	    long originateTime = readTimeStamp(buffer, ORIGINATE_TIME_OFFSET);
	    long receiveTime = readTimeStamp(buffer, RECEIVE_TIME_OFFSET);
	    long transmitTime = readTimeStamp(buffer, TRANSMIT_TIME_OFFSET);
	    long roundTripTime = responseTicks - requestTicks - (transmitTime - receiveTime);
	    // receiveTime = originateTime + transit + skew
	    // responseTime = transmitTime + transit - skew
	    // clockOffset = ((receiveTime - originateTime) + (transmitTime - responseTime))/2
	    // = ((originateTime + transit + skew - originateTime) +
	    // (transmitTime - (transmitTime + transit - skew)))/2
	    // = ((transit + skew) + (transmitTime - transmitTime - transit + skew))/2
	    // = (transit + skew - transit + skew)/2
	    // = (2 * skew)/2 = skew
	    clockOffset = ((receiveTime - originateTime) + (transmitTime - responseTime)) / 2;
	    // if (Config.LOGD) Log.d(TAG, "round trip: " + roundTripTime + " ms");
	    // if (Config.LOGD) Log.d(TAG, "clock offset: " + clockOffset + " ms");

	    // save our results - use the times on this side of the network latency
	    // (response rather than request time)
	    mNtpTime = responseTime + clockOffset;
	    mNtpTimeReference = responseTicks;
	    mRoundTripTime = roundTripTime;
	} catch (Exception e) {
	    Log.w(TAG, "Request time failed: " + e);
	    return false;
	}

	return true;
    }

    /**
     * Get the current time, based on NTP clockOffset if or System time
     * 
     * @return the ntp time from the worldwide pool if available else the system time
     */
    public long getTime() {
	// get the clock offset
	if (clockOffset == -1) {
	    if (requestTime(HOST_WORLDWIDE, 1000)) {
		return getNtpTime();
	    } else {
		clockOffset = 0;
	    }
	}

	return System.currentTimeMillis() + clockOffset;
    }

    /**
     * Returns the time computed from the NTP transaction.
     * 
     * @return time value computed from NTP server response.
     */
    public long getNtpTime() {
    	//Log.d(TAG,"SNTP time requested");
    	//return 1412175540000l;
    	return mNtpTime;
    }

    /**
     * Returns the reference clock value (value of SystemClock.elapsedRealtime()) corresponding to
     * the NTP time.
     * 
     * @return reference clock corresponding to the NTP time.
     */
    public long getNtpTimeReference() {
	return mNtpTimeReference;
    }

    /**
     * Returns the round trip time of the NTP transaction
     * 
     * @return round trip time in milliseconds.
     */
    public long getRoundTripTime() {
	return mRoundTripTime;
    }

    /**
     * Reads an unsigned 32 bit big endian number from the given offset in the buffer.
     */
    private long read32(byte[] buffer, int offset) {
	byte b0 = buffer[offset];
	byte b1 = buffer[offset + 1];
	byte b2 = buffer[offset + 2];
	byte b3 = buffer[offset + 3];

	// convert signed bytes to unsigned values
	int i0 = ((b0 & 0x80) == 0x80 ? (b0 & 0x7F) + 0x80 : b0);
	int i1 = ((b1 & 0x80) == 0x80 ? (b1 & 0x7F) + 0x80 : b1);
	int i2 = ((b2 & 0x80) == 0x80 ? (b2 & 0x7F) + 0x80 : b2);
	int i3 = ((b3 & 0x80) == 0x80 ? (b3 & 0x7F) + 0x80 : b3);

	return ((long) i0 << 24) + ((long) i1 << 16) + ((long) i2 << 8) + (long) i3;
    }

    /**
     * Reads the NTP time stamp at the given offset in the buffer and returns it as a system time
     * (milliseconds since January 1, 1970).
     */
    private long readTimeStamp(byte[] buffer, int offset) {
	long seconds = read32(buffer, offset);
	long fraction = read32(buffer, offset + 4);
	return ((seconds - OFFSET_1900_TO_1970) * 1000) + ((fraction * 1000L) / 0x100000000L);
    }

    /**
     * Writes system time (milliseconds since January 1, 1970) as an NTP time stamp at the given
     * offset in the buffer.
     */
    private void writeTimeStamp(byte[] buffer, int offset, long time) {
	long seconds = time / 1000L;
	long milliseconds = time - seconds * 1000L;
	seconds += OFFSET_1900_TO_1970;

	// write seconds in big endian format
	buffer[offset++] = (byte) (seconds >> 24);
	buffer[offset++] = (byte) (seconds >> 16);
	buffer[offset++] = (byte) (seconds >> 8);
	buffer[offset++] = (byte) (seconds >> 0);

	long fraction = milliseconds * 0x100000000L / 1000L;
	// write fraction in big endian format
	buffer[offset++] = (byte) (fraction >> 24);
	buffer[offset++] = (byte) (fraction >> 16);
	buffer[offset++] = (byte) (fraction >> 8);
	// low order bits should be random data
	buffer[offset++] = (byte) (Math.random() * 255.0);
    }
} public class PeriodicPollAlarmReceiver implements Runnable {

    private final PeriodicPollingSensor sensor;

    public PeriodicPollAlarmReceiver(PeriodicPollingSensor sensor) {
        this.sensor = sensor;
    }

    @Override
    public void run() {
        sensor.doSample();
    }

    /**
     * Starts periodically calling {@link BasePeriodicPollingSensor#doSample()}. Schedules a
     * periodic alarm, and registers itself as alarm receiver.
     * 
     * @param context
     *            Application context, used to register as BroadcastReceiver, and to schedule alarms
     */
    public void start(Context context) {

        // schedule alarm broadcasts
        long interval = sensor.getSampleRate();
        Scheduler.getInstance(context).register(this, interval, (long) (interval * 0.2));
    }

    /**
     * Stops the periodic polling. Unregisters as BroadcastReceiver and stops the periodic alarm
     * broadcasts.
     * 
     * @param context
     *            Application context, used to modify broadcast registration and alarm schedule
     */
    public void stop(Context context) {

        // stop alarm broadcasts
        Scheduler.getInstance(context).unregister(this);
    }
} public interface PeriodicPollingSensor extends SenseSensor {

    /**
     * Called by the {@link PeriodicPollAlarmReceiver} to check if the sensor is still active.
     * 
     * @return true if the sensor is active
     */
    public abstract boolean isActive();

    /**
     * Performs a sample. Called by the {@link PeriodicPollAlarmReceiver}.
     */
    public abstract void doSample();

} public class SensorDataPoint {

    public enum DataType {
        INT, FLOAT, BOOL, DOUBLE, STRING, ARRAYLIST, JSON, JSONSTRING, FILE, SENSOREVENT
    };

    /**
     * Name of the sensor that produced the data point.
     */
    public String sensorName;
    /**
     * Description of the sensor that produces the data point. Corresponds to the "sensor_type"
     * field in the CommonSense API.
     */
    public String sensorDescription;
    /**
     * Time stamp of the data point.
     */
    public long timeStamp;
    private DataType dataType;
    private Object value;

    /**
     * @see #SensorDataPoint(int)
     */
    public SensorDataPoint(Object value) {       
        this.value = value;
    }
    
    /**
     * @see #SensorDataPoint(int)
     */
    public SensorDataPoint(ArrayList<SensorDataPoint> value) {
        dataType = DataType.ARRAYLIST;
        this.value = value;
    }

    /**
     * @see #SensorDataPoint(int)
     */
    public SensorDataPoint(Boolean value) {
        dataType = DataType.BOOL;
        this.value = value;
    }

    /**
     * @see #SensorDataPoint(int)
     */
    public SensorDataPoint(double value) {
        dataType = DataType.DOUBLE;
        this.value = value;
    }

    /**
     * @see #SensorDataPoint(int)
     */
    public SensorDataPoint(float value) {
        dataType = DataType.FLOAT;
        this.value = value;
    }

    /**
     * Sets the value of the SensorDataPoint based on the input value the dataType. The dataType can
     * be changed by {@link #setDataType(DataType)}.
     * 
     * @param value
     *            The input value which is stored in the SensorDataPoint
     */
    public SensorDataPoint(int value) {
        dataType = DataType.INT;
        this.value = value;
    }

    /**
     * @see #setValue(int)
     */
    public SensorDataPoint(JSONObject value) {
        dataType = DataType.JSON;
        this.value = value;
    }

    /**
     * @see #setValue(int)
     */
    public SensorDataPoint(SensorEvent value) {
        dataType = DataType.SENSOREVENT;
        this.value = value;
    }

    /**
     * @see #setValue(int)
     */
    public SensorDataPoint(String value) {
        dataType = DataType.STRING;
        this.value = value;
    }

    /**
     * @see #getIntValue()
     */
    @SuppressWarnings("unchecked")
    public ArrayList<SensorDataPoint> getArrayValue() {
        return (ArrayList<SensorDataPoint>) value;
    }

    /**
     * @see #getIntValue()
     */
    public Boolean getBoolValue() {
        return (Boolean) value;
    }

    /**
     * Returns the dataType of the SensorDataPoint
     * 
     * @return DataTypes
     */
    public DataType getDataType() {
        return dataType;
    }

    /**
     * @see #getIntValue()
     */
    public double getDoubleValue() {
        return (Double) value;
    }

    /**
     * @see #getIntValue()
     */
    public float getFloatValue() {
        return (Float) value;
    }

    /**
     * Return the value of the SensorDataPoint
     * 
     * @return value
     */
    public int getIntValue() {
        return (Integer) value;
    }

    /**
     * @see #getIntValue()
     */
    public JSONObject getJSONValue() {
        return (JSONObject) value;
    }

    /**
     * @see #getIntValue()
     */
    public SensorEvent getSensorEventValue() {
        return (SensorEvent) value;
    }

    /**
     * @see #getIntValue()
     */
    public String getStringValue() {
        return (String) value;
    }

    /**
     * Sets the data type.
     * 
     * This method overrides the dataType which has been set automatically. This method can be used
     * when a more precise dataType like FILE is needed.
     * 
     * @param dataType
     *            The dataType of the SensorDataPoint
     */
    public void setDataType(DataType dataType) {
        this.dataType = dataType;
    }
} public enum DataType {
    INT, FLOAT, BOOL, DOUBLE, STRING, ARRAYLIST, JSON, JSONSTRING, FILE, SENSOREVENT
}; public abstract class BaseDataProducer implements DataProducer {

	protected final String TAG = "BaseDataProducer";
	
    /** The DataProcessors which are subscribed to this sensor for sensor data */
    protected List<DataConsumer> mSubscribers = new ArrayList<DataConsumer>();

    /**
     * Adds a data consumer as subscriber to this data producer. This method is synchronized to
     * prevent concurrent modifications to the list of subscribers.
     * 
     * @param consumer
     *            The DataProcessor that wants the sensor data as input
     * @return <code>true</code> if the DataConsumer was successfully subscribed
     */
    @Override
    public synchronized boolean addSubscriber(DataConsumer consumer) {
        if (!hasSubscriber(consumer)) {
            return mSubscribers.add(consumer);
        } else {
            return false;
        }
    }

    /**
     * Checks subscribers if the sample is complete. This method uses
     * {@link DataConsumer#isSampleComplete()} to see if the sample is completed, or if the data
     * processors need more data.
     * 
     * @return true when all the subscribers have complete samples
     */
    protected synchronized boolean checkSubscribers() {
        boolean complete = true;
        for (DataConsumer subscriber : mSubscribers) {
            if (subscriber != null) {
                complete &= subscriber.isSampleComplete();
            }
        }
        return complete;
    }

    @Override
    public synchronized boolean hasSubscriber(DataConsumer consumer) {
        for (DataConsumer subscriber : mSubscribers) {
            if (subscriber != null && subscriber.equals(consumer)) {
                return true;
            }
        }
        return false;
    }

    @Override
    public boolean hasSubscribers() {
        return !mSubscribers.isEmpty();
    }

    /**
     * Notifies subscribers that a new sample is starting. This method calls
     * {@link DataConsumer#startNewSample()} on each subscriber.
     */
    protected synchronized void notifySubscribers() {
        for (DataConsumer subscriber : mSubscribers) {
            if (subscriber != null) {
                subscriber.startNewSample();
            }
        }
    }

    /**
     * Removes a data subscriber if present. This method is synchronized to prevent concurrent
     * modifications.
     * 
     * @param subscriber
     *            The DataConsumer that should be unsubscribed
     */
    @Override
    public synchronized boolean removeSubscriber(DataConsumer consumer) {
        if (mSubscribers.contains(consumer)) {
            return mSubscribers.remove(consumer);
        }
        return true;
    }

    /**
     * Sends data to all subscribed data consumers.
     * 
     * @param dataPoint
     *            The SensorDataPoint to send
     */
    // TODO: do a-sync
    protected synchronized void sendToSubscribers(SensorDataPoint dataPoint) {
        for (DataConsumer subscriber : mSubscribers) {
            if (subscriber != null && !subscriber.isSampleComplete()) {
            	try
            	{
            		subscriber.onNewData(dataPoint);
            	}
            	catch(Exception e)
            	{
            		Log.e(TAG, "Error sending data to subscriber.", e);
            	}
            }
        }
    }
} public abstract class BaseSensor extends BaseDataProducer implements DataProducer, SenseSensor {

	/** The delay value for the sample rate */
    private long sampleDelay = 0;

    @Override
    public long getSampleRate() {
    	return sampleDelay;
    }

    @Override
    public void setSampleRate(long sampleDelay) {
    	this.sampleDelay = sampleDelay;
    }

} public interface DataProducer {

    /**
     * Adds a data consumer as subscriber for data from this producer. If an instance of the
     * DataConsumer is already subscribed, then it should be ignored.
     * 
     * @param subscriber
     *            The DataConsumer that wants the sensor data as input
     * @return <code>true</code> if the DataConsumer was successfully subscribed.
     */
    public abstract boolean addSubscriber(DataConsumer subscriber);

    /**
     * Checks if a DataProcessor has been added as a subscriber.
     * 
     * @param subscriber
     * @return <code>true</code> if the DataConsumer is listed as a subscriber
     */
    public abstract boolean hasSubscriber(DataConsumer subscriber);

    /**
     * @return <code>true</code> if there are subscribers
     */
    public abstract boolean hasSubscribers();

    /**
     * Removes a data subscriber if present.
     * 
     * @param subscriber
     *            The DataConsumer that should be unsubscribed
     * @return <code>true</code> if the subscriber was removed
     */
    public abstract boolean removeSubscriber(DataConsumer subscriber);

}
import android.annotation.SuppressLint;
import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
import android.media.AudioFormat;
import android.media.AudioRecord;
import android.media.MediaRecorder;
import android.media.MediaRecorder.OnInfoListener;
import android.os.Build;
import android.os.Environment;
import android.os.Handler;
import android.os.Looper;
import android.telephony.PhoneStateListener;
import android.telephony.TelephonyManager;
import android.util.Log;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import java.io.File;
import java.math.BigDecimal;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Date;
import java.util.Locale;
import nl.sense_os.service.MsgHandler;
import nl.sense_os.service.R;
import nl.sense_os.service.constants.SenseDataTypes;
import nl.sense_os.service.constants.SensePrefs;
import nl.sense_os.service.constants.SensePrefs.Main.Ambience;
import nl.sense_os.service.constants.SensorData.DataPoint;
import nl.sense_os.service.constants.SensorData.SensorNames;
import nl.sense_os.service.ctrl.Controller;
import nl.sense_os.service.provider.SNTP;
import nl.sense_os.service.shared.PeriodicPollAlarmReceiver;
import nl.sense_os.service.shared.PeriodicPollingSensor;
import nl.sense_os.service.shared.SensorDataPoint;
import nl.sense_os.service.shared.SensorDataPoint.DataType;
import nl.sense_os.service.subscription.BaseDataProducer;
import nl.sense_os.service.subscription.BaseSensor;
import nl.sense_os.service.subscription.DataProducer;
						.getString(R.string.action_sense_new_data));
				i.putExtra(DataPoint.SENSOR_NAME, SensorNames.MIC);
				i.putExtra(DataPoint.VALUE, fileName);
				i.putExtra(DataPoint.DATA_TYPE, SenseDataTypes.FILE);
				i.putExtra(DataPoint.TIMESTAMP, dataPoint.timeStamp);
				i.setPackage(context.getPackageName());
				context.startService(i);				

			} catch (Exception e) {
				e.printStackTrace();
			}
		}
	}

	public class NoiseBurstSensor extends BaseDataProducer {
		private int INTERVAL = 100; // ms 
		public NoiseBurstSensor() {

		}
		public void addData(float [] samples, int recording_time, long start_time)
		{
			if(samples.length <= 0)
				return;
			JSONObject burst = new JSONObject();

			try {
				burst.put("interval", INTERVAL);
				burst.put("data", calculateDb(samples, (int)(((double)samples.length/(double)recording_time)*(double)INTERVAL)));
				sendData(burst, start_time);
			} catch (JSONException e) {
				Log.e(TAG,"Error in computing burst data" ,e);
			}    		
		}

		private JSONArray calculateDb(float[] samples, int step_size) 
		{
			JSONArray noise_burst = new JSONArray();    	
			try
			{
				for (int i = 0; i < samples.length; i+=step_size) {

					double dB = 0;
					double ldb = 0;

					for (int x = 0; x < step_size ; ++x) {
						ldb += ((double) samples[i+x] * (double) samples[i+x]);
					}

					ldb /= (double) step_size;
					dB = 10.0 * Math.log10(ldb);

					if (dB == Double.POSITIVE_INFINITY)
						noise_burst.put(140);
					else if (dB != Double.NaN && dB != Double.NEGATIVE_INFINITY) // normal case
						noise_burst.put(Math.round(dB));
					else	
						noise_burst.put(0);  

				}
			}
			catch(Exception e)
			{
				Log.e(TAG, "Error in calculating burst noise", e);
			}
			return  noise_burst;
		}

		public void sendData(JSONObject data, long startTimestamp)
		{
			notifySubscribers();
			SensorDataPoint dataPoint = new SensorDataPoint(data);
			dataPoint.sensorName = SensorNames.NOISE_BURST;
			dataPoint.sensorDescription =  "noise (dB)";
			dataPoint.timeStamp = startTimestamp;
			sendToSubscribers(dataPoint);

			// pass message to the MsgHandler
			// get sample rate from preferences
			SharedPreferences mainPrefs = context.getSharedPreferences(SensePrefs.MAIN_PREFS, Context.MODE_PRIVATE);
			if(!mainPrefs.getBoolean(Ambience.DONT_UPLOAD_BURSTS, true))
			{
				Intent sensorData = new Intent(context.getString(R.string.action_sense_new_data));
				sensorData.putExtra(DataPoint.SENSOR_NAME, SensorNames.NOISE_BURST);
				sensorData.putExtra(DataPoint.SENSOR_DESCRIPTION, "noise (dB)");
				sensorData.putExtra(DataPoint.VALUE, data.toString());
				sensorData.putExtra(DataPoint.DATA_TYPE, SenseDataTypes.JSON);
				sensorData.putExtra(DataPoint.TIMESTAMP, startTimestamp);
				sensorData.setPackage(context.getPackageName());
				context.startService(sensorData);
			}
		}
	};

	private static NoiseSensor instance = null;

	private static final String TAG = "Sense NoiseSensor";

	/**
	 * Factory method to get the singleton instance.
	 * 
	 * @param context
	 * @return instance
	 */
	public static NoiseSensor getInstance(Context context) {
		if (instance == null) {
			instance = new NoiseSensor(context);
		}
		return instance;
	}

	private boolean active = false;
	private boolean calling = false;
	private Context context;
	private Handler soundStreamHandler = new Handler(Looper.getMainLooper());
	private SoundStreamJob soundStreamJob = null;
	private Handler noiseSampleHandler = new Handler();
	private NoiseSampleJob noiseSampleJob = null;
	private LoudnessSensor loudnessSensor;
	private NoiseBurstSensor noiseBurstSensor = new NoiseBurstSensor();
	private AutoCalibratedNoiseSensor autoCalibratedNoiseSensor;
	private Controller controller;