<commit_before>/**************************************************************************************************
 * Copyright (C) 2010 Sense Observation Systems, Rotterdam, the Netherlands. All rights reserved. *
 *************************************************************************************************/
package nl.sense_os.service;

import java.net.URLEncoder;
import java.util.Map;
import java.util.Timer;
import java.util.TimerTask;

import nl.sense_os.service.ambience.CameraLightSensor;
import nl.sense_os.service.ambience.LightSensor;
import nl.sense_os.service.ambience.NoiseSensor;
import nl.sense_os.service.ambience.PressureSensor;
import nl.sense_os.service.ambience.TemperatureSensor;
import nl.sense_os.service.commonsense.PhoneSensorRegistrator;
import nl.sense_os.service.commonsense.SenseApi;
import nl.sense_os.service.constants.SensePrefs;
import nl.sense_os.service.constants.SensePrefs.Auth;
import nl.sense_os.service.constants.SensePrefs.Main.Advanced;
import nl.sense_os.service.constants.SensePrefs.Main.Ambience;
import nl.sense_os.service.constants.SensePrefs.Main.External;
import nl.sense_os.service.constants.SensePrefs.Main.PhoneState;
import nl.sense_os.service.constants.SensePrefs.Status;
import nl.sense_os.service.constants.SenseUrls;
import nl.sense_os.service.deviceprox.DeviceProximity;
import nl.sense_os.service.external_sensors.NewOBD2DeviceConnector;
import nl.sense_os.service.external_sensors.ZephyrBioHarness;
import nl.sense_os.service.external_sensors.ZephyrHxM;
import nl.sense_os.service.location.LocationSensor;
import nl.sense_os.service.motion.MotionSensor;
import nl.sense_os.service.phonestate.BatterySensor;
import nl.sense_os.service.phonestate.PhoneActivitySensor;
import nl.sense_os.service.phonestate.ProximitySensor;
import nl.sense_os.service.phonestate.SensePhoneState;
import nl.sense_os.service.provider.SNTP;

import org.json.JSONObject;

import android.app.Activity;
import android.app.AlarmManager;
import android.app.Notification;
import android.app.Service;
import android.content.Intent;
import android.content.SharedPreferences;
import android.content.SharedPreferences.Editor;
import android.content.pm.PackageInfo;
import android.os.Build;
import android.os.Handler;
import android.os.HandlerThread;
import android.os.IBinder;
import android.os.Looper;
import android.os.Message;
import android.os.Process;
import android.os.RemoteException;
import android.util.Log;
import android.widget.Toast;

public class SenseService extends Service {

    /**
     * Implementation of the service's AIDL interface.
     */
    private class SenseServiceStub extends ISenseService.Stub {

        private static final String TAG = "SenseServiceStub";

        @Override
        public void changeLogin(final String username, final String password,
                final ISenseServiceCallback callback) throws RemoteException {
            // Log.v(TAG, "Change login");

            // perform login on separate thread and respond via callback
            new Thread() {

                @Override
                public void run() {
                    int result = SenseService.this.changeLogin(username, password);
                    try {
                        callback.onChangeLoginResult(result);
                    } catch (RemoteException e) {
                        Log.e(TAG, "Failed to call back to bound activity after login change: " + e);
                    }
                }
            }.start();
        }

        @Override
        public boolean getPrefBool(String key, boolean defValue) throws RemoteException {
            // Log.v(TAG, "Get preference: " + key);
            SharedPreferences prefs;
            if (key.equals(Status.AMBIENCE) || key.equals(Status.DEV_PROX)
                    || key.equals(Status.EXTERNAL) || key.equals(Status.LOCATION)
                    || key.equals(Status.MAIN) || key.equals(Status.MOTION)
                    || key.equals(Status.PHONESTATE) || key.equals(Status.POPQUIZ)
                    || key.equals(Status.AUTOSTART)) {
                prefs = getSharedPreferences(SensePrefs.STATUS_PREFS, MODE_PRIVATE);
            } else {
                prefs = getSharedPreferences(SensePrefs.MAIN_PREFS, MODE_PRIVATE);
            }

            // return the preference value
            try {
                return prefs.getBoolean(key, defValue);
            } catch (ClassCastException e) {
                return defValue;
            }
        }

        @Override
        public float getPrefFloat(String key, float defValue) throws RemoteException {
            // Log.v(TAG, "Get preference: " + key);
            SharedPreferences prefs = getSharedPreferences(SensePrefs.MAIN_PREFS, MODE_PRIVATE);
            try {
                return prefs.getFloat(key, defValue);
            } catch (ClassCastException e) {
                return defValue;
            }
        }

        @Override
        public int getPrefInt(String key, int defValue) throws RemoteException {
            // Log.v(TAG, "Get preference: " + key);
            SharedPreferences prefs = getSharedPreferences(SensePrefs.MAIN_PREFS, MODE_PRIVATE);
            try {
                return prefs.getInt(key, defValue);
            } catch (ClassCastException e) {
                return defValue;
            }
        }

        @Override
        public long getPrefLong(String key, long defValue) throws RemoteException {
            // Log.v(TAG, "Get preference: " + key);
            SharedPreferences prefs;
            if (key.equals(Auth.SENSOR_LIST_TIME)) {
                prefs = getSharedPreferences(SensePrefs.AUTH_PREFS, MODE_PRIVATE);
            } else {
                prefs = getSharedPreferences(SensePrefs.MAIN_PREFS, MODE_PRIVATE);
            }

            try {
                return prefs.getLong(key, defValue);
            } catch (ClassCastException e) {
                return defValue;
            }
        }

        @Override
        public String getPrefString(String key, String defValue) throws RemoteException {
            // Log.v(TAG, "Get preference: " + key);
            SharedPreferences prefs;
            if (key.equals(Auth.LOGIN_COOKIE) || key.equals(Auth.LOGIN_PASS)
                    || key.equals(Auth.LOGIN_USERNAME) || key.equals(Auth.SENSOR_LIST)
                    || key.equals(Auth.DEVICE_ID) || key.equals(Auth.PHONE_IMEI)
                    || key.equals(Auth.PHONE_TYPE)) {
                prefs = getSharedPreferences(SensePrefs.AUTH_PREFS, MODE_PRIVATE);
            } else {
                // all other preferences
                prefs = getSharedPreferences(SensePrefs.MAIN_PREFS, MODE_PRIVATE);
            }

            // return the preference value
            try {
                return prefs.getString(key, defValue);
            } catch (ClassCastException e) {
                return defValue;
            }
        }

        @Override
        public String getSessionId(String appSecret) throws RemoteException {
            try {
                return SenseApi.getSessionId(SenseService.this, appSecret);
            } catch (IllegalAccessException e) {
                throw new RemoteException();
            }
        }

        @Override
        public void getStatus(ISenseServiceCallback callback) throws RemoteException {
            callback.statusReport(state.getStatusCode());
        }

        public void logout() {
            SenseService.this.logout();
        }

        @Override
        public void register(final String username, final String password, final String name,
                final String surname, final String email, final String mobile,
                final ISenseServiceCallback callback) throws RemoteException {
            // Log.v(TAG, "Register: '" + username + "'");

            // perform registration on separate thread and respond via callback
            new Thread() {

                @Override
                public void run() {
                    int result = SenseService.this.register(username, password, name, surname,
                            email, mobile);
                    try {
                        callback.onRegisterResult(result);
                    } catch (RemoteException e) {
                        Log.e(TAG, "Failed to call back to bound activity after registration: " + e);
                    }
                }
            }.start();
        }

        @Override
        public void setPrefBool(String key, final boolean value) throws RemoteException {
            // Log.v(TAG, "Set preference: '" + key + "': '" + value + "'");

            SharedPreferences prefs;
            if (key.equals(Status.AMBIENCE) || key.equals(Status.DEV_PROX)
                    || key.equals(Status.EXTERNAL) || key.equals(Status.LOCATION)
                    || key.equals(Status.MAIN) || key.equals(Status.MOTION)
                    || key.equals(Status.PHONESTATE) || key.equals(Status.POPQUIZ)
                    || key.equals(Status.AUTOSTART)) {
                prefs = getSharedPreferences(SensePrefs.STATUS_PREFS, MODE_PRIVATE);
            } else {
                prefs = getSharedPreferences(SensePrefs.MAIN_PREFS, MODE_PRIVATE);
            }

            // store value
            boolean stored = prefs.edit().putBoolean(key, value).commit();
            if (stored == false) {
                Log.w(TAG, "Preference '" + key + "' not stored!");
            } else if (key.equals(Advanced.DEV_MODE) && state.isLoggedIn()) {
                logout();
            } else if (key.equals(Advanced.USE_COMMONSENSE)) {
                // login on a separate thread
                new Thread() {
                    public void run() {
                        if (value) {
                            Log.w(TAG, "USE_COMMONSENSE setting changed: try to log in...");
                            login();
                        } else {
                            Log.w(TAG, "USE_COMMONSENSE setting changed: logging out...");
                            logout();
                        }
                    }
                }.start();
            }
        }

        @Override
        public void setPrefFloat(String key, float value) throws RemoteException {
            // Log.v(TAG, "Set preference: " + key + ": \'" + value + "\'");
            SharedPreferences prefs = getSharedPreferences(SensePrefs.MAIN_PREFS, MODE_PRIVATE);

            // store value
            boolean stored = prefs.edit().putFloat(key, value).commit();
            if (stored == false) {
                Log.w(TAG, "Preference " + key + " not stored!");
            }
        }

        @Override
        public void setPrefInt(String key, int value) throws RemoteException {
            // Log.v(TAG, "Set preference: " + key + ": \'" + value + "\'");
            SharedPreferences prefs = getSharedPreferences(SensePrefs.MAIN_PREFS, MODE_PRIVATE);

            // store value
            boolean stored = prefs.edit().putFloat(key, value).commit();
            if (stored == false) {
                Log.w(TAG, "Preference " + key + " not stored!");
            }
        }

        @Override
        public void setPrefLong(String key, long value) throws RemoteException {
            // Log.v(TAG, "Set preference: " + key + ": \'" + value + "\'");
            SharedPreferences prefs;
            if (key.equals(Auth.SENSOR_LIST_TIME)) {
                prefs = getSharedPreferences(SensePrefs.AUTH_PREFS, MODE_PRIVATE);
            } else {
                prefs = getSharedPreferences(SensePrefs.MAIN_PREFS, MODE_PRIVATE);
            }

            // store value
            boolean stored = prefs.edit().putLong(key, value).commit();
            if (stored == false) {
                Log.w(TAG, "Preference " + key + " not stored!");
            }
        }

        @Override
        public void setPrefString(String key, String value) throws RemoteException {
            // Log.v(TAG, "Set preference: " + key + ": \'" + value + "\'");
            SharedPreferences prefs;
            if (key.equals(Auth.LOGIN_COOKIE) || key.equals(Auth.LOGIN_PASS)
                    || key.equals(Auth.LOGIN_USERNAME) || key.equals(Auth.SENSOR_LIST)
                    || key.equals(Auth.DEVICE_ID) || key.equals(Auth.PHONE_IMEI)
                    || key.equals(Auth.PHONE_TYPE)) {
                prefs = getSharedPreferences(SensePrefs.AUTH_PREFS, MODE_PRIVATE);
            } else {
                // all other preferences
                prefs = getSharedPreferences(SensePrefs.MAIN_PREFS, MODE_PRIVATE);
            }

            // store value
            boolean stored = prefs.edit().putString(key, value).commit();
            if (stored == false) {
                Log.w(TAG, "Preference " + key + " not stored!");
            }

            // special check for sync and sample rate changes
            if (key.equals(SensePrefs.Main.SAMPLE_RATE)) {
                onSampleRateChange();
            } else if (key.equals(SensePrefs.Main.SYNC_RATE)) {
                onSyncRateChange();
            }
        }

        @Override
        public void toggleAmbience(boolean active) {
            // Log.v(TAG, "Toggle ambience: " + active);
            SharedPreferences prefs = getSharedPreferences(SensePrefs.STATUS_PREFS, MODE_PRIVATE);
            prefs.edit().putBoolean(Status.AMBIENCE, active).commit();
            SenseService.this.toggleAmbience(active);
        }

        @Override
        public void toggleDeviceProx(boolean active) {
            // Log.v(TAG, "Toggle neighboring devices: " + active);
            SharedPreferences prefs = getSharedPreferences(SensePrefs.STATUS_PREFS, MODE_PRIVATE);
            prefs.edit().putBoolean(Status.DEV_PROX, active).commit();
            SenseService.this.toggleDeviceProx(active);
        }

        @Override
        public void toggleExternalSensors(boolean active) {
            // Log.v(TAG, "Toggle external sensors: " + active);
            SharedPreferences prefs = getSharedPreferences(SensePrefs.STATUS_PREFS, MODE_PRIVATE);
            prefs.edit().putBoolean(Status.EXTERNAL, active).commit();
            SenseService.this.toggleExternalSensors(active);
        }

        @Override
        public void toggleLocation(boolean active) {
            // Log.v(TAG, "Toggle location: " + active);
            SharedPreferences prefs = getSharedPreferences(SensePrefs.STATUS_PREFS, MODE_PRIVATE);
            prefs.edit().putBoolean(Status.LOCATION, active).commit();
            SenseService.this.toggleLocation(active);
        }

        @Override
        public void toggleMain(boolean active) {
            // Log.v(TAG, "Toggle main: " + active);
            SharedPreferences prefs = getSharedPreferences(SensePrefs.STATUS_PREFS, MODE_PRIVATE);
            prefs.edit().putBoolean(Status.MAIN, active).commit();
            SenseService.this.toggleMain(active);
        }

        @Override
        public void toggleMotion(boolean active) {
            // Log.v(TAG, "Toggle motion: " + active);
            SharedPreferences prefs = getSharedPreferences(SensePrefs.STATUS_PREFS, MODE_PRIVATE);
            prefs.edit().putBoolean(Status.MOTION, active).commit();
            SenseService.this.toggleMotion(active);
        }

        @Override
        public void togglePhoneState(boolean active) {
            // Log.v(TAG, "Toggle phone state: " + active);
            SharedPreferences prefs = getSharedPreferences(SensePrefs.STATUS_PREFS, MODE_PRIVATE);
            prefs.edit().putBoolean(Status.PHONESTATE, active).commit();
            SenseService.this.togglePhoneState(active);
        }

        @Override
        public void togglePopQuiz(boolean active) {
            // Log.v(TAG, "Toggle questionnaire: " + active);
            SharedPreferences prefs = getSharedPreferences(SensePrefs.STATUS_PREFS, MODE_PRIVATE);
            prefs.edit().putBoolean(Status.POPQUIZ, active).commit();
            SenseService.this.togglePopQuiz(active);
        }
    }

    private static final String TAG = "Sense Service";

    /**
     * Intent action to force a re-login attempt when the service is started.
     */
    public static final String EXTRA_RELOGIN = "relogin";

    /**
     * Intent action for broadcasts that the service state has changed.
     */
    public final static String ACTION_SERVICE_BROADCAST = "nl.sense_os.service.Broadcast";

    private final ISenseService.Stub binder = new SenseServiceStub();

    private ServiceStateHelper state;

    private BatterySensor batterySensor;
    private DeviceProximity deviceProximity;
    private LightSensor lightSensor;
    private CameraLightSensor cameraLightSensor;
    private TemperatureSensor temperatureSensor;
    private LocationSensor locListener;
    private MotionSensor motionSensor;
    private NoiseSensor noiseSensor;
    private PhoneActivitySensor phoneActivitySensor;
    private PressureSensor pressureSensor;
    private ProximitySensor proximitySensor;
    private SensePhoneState phoneStateListener;
    private ZephyrBioHarness es_bioHarness;
    private ZephyrHxM es_HxM;
    private NewOBD2DeviceConnector es_obd2sensor;

    /**
     * Handler on main application thread to display toasts to the user.
     */
    private final Handler toastHandler = new Handler(Looper.getMainLooper());

    /*
     * fields that handle verification of the sensor IDs for communication with CommonSense
     */
    private final PhoneSensorRegistrator sensorVerifier = new PhoneSensorRegistrator(this);
    private final Timer sensorVerifyTimer = new Timer();
    private TimerTask sensorVerifyTask;
    private long sensorVerifyTimeout = 15000;

    // separate threads for the sensing modules
    private HandlerThread ambienceThread, motionThread, deviceProxThread, extSensorsThread,
            locationThread, phoneStateThread;

    /**
     * Changes login of the Sense service. Removes "private" data of the previous user from the
     * preferences. Can be called by Activities that are bound to the service.
     * 
     * @param username
     *            Username
     * @param password
     *            <PASSWORD>
     * @return 0 if login completed successfully, -2 if login was forbidden, and -1 for any other
     *         errors.
     */
    private int changeLogin(String username, String password) {

        logout();

        // save new username and password in the preferences
        Editor authEditor = getSharedPreferences(SensePrefs.AUTH_PREFS, MODE_PRIVATE).edit();
        authEditor.putString(Auth.LOGIN_USERNAME, username);
        authEditor.putString(Auth.LOGIN_PASS, password);
        authEditor.commit();

        return login();
    }

    private void logout() {
        Log.v(TAG, "Log out...");

        // stop active sensing components
        stopSensorModules();

        // clear cached settings of the previous user (e.g. device id)
        Editor authEditor = getSharedPreferences(SensePrefs.AUTH_PREFS, MODE_PRIVATE).edit();
        authEditor.clear();
        authEditor.commit();

        // log out before changing to a new user
        onLogOut();
    }

    /**
     * Checks if the installed Sense Platform application has an update available, alerting the user
     * via a Toast message.
     */
    private void checkVersion() {
        try {
            PackageInfo packageInfo = getPackageManager().getPackageInfo("nl.sense_os.app", 0);
            String versionName = URLEncoder.encode(packageInfo.versionName);
            Log.i(TAG, "Running Sense Platform version '" + versionName + "'");

            if (versionName.equals("unstable")) {
                return;
            }

            String url = SenseUrls.VERSION + "?version=" + versionName;
            Map<String, String> response = SenseApi.request(this, url, null, null);
            JSONObject content = new JSONObject(response.get("content"));

            if (content.getString("message").length() > 0) {
                Log.i(TAG, "Newer Sense Platform version available: " + content.toString());
                showToast(content.getString("message"));
            }

        } catch (Exception e) {
            if (null != e.getMessage()) {
                Log.e(TAG, "Failed to get Sense Platform version! Message: " + e.getMessage());
            } else {
                Log.e(TAG, "Failed to get Sense Platform version!", e);
            }
        }
    }

    /**
     * Tries to login using the username and password from the private preferences and updates the
     * {@link #isLoggedIn} status accordingly. Can also be called from Activities that are bound to
     * the service.
     * 
     * @return 0 if login completed successfully, -2 if login was forbidden, and -1 for any other
     *         errors.
     */
    private int login() {
        Log.v(TAG, "Log in...");<commit_msg>less scary logging about sense platform version checking<commit_after>/**************************************************************************************************
 * Copyright (C) 2010 Sense Observation Systems, Rotterdam, the Netherlands. All rights reserved. *
 *************************************************************************************************/
package nl.sense_os.service;

import java.net.URLEncoder;
import java.util.Map;
import java.util.Timer;
import java.util.TimerTask;

import nl.sense_os.service.ambience.CameraLightSensor;
import nl.sense_os.service.ambience.LightSensor;
import nl.sense_os.service.ambience.NoiseSensor;
import nl.sense_os.service.ambience.PressureSensor;
import nl.sense_os.service.ambience.TemperatureSensor;
import nl.sense_os.service.commonsense.PhoneSensorRegistrator;
import nl.sense_os.service.commonsense.SenseApi;
import nl.sense_os.service.constants.SensePrefs;
import nl.sense_os.service.constants.SensePrefs.Auth;
import nl.sense_os.service.constants.SensePrefs.Main.Advanced;
import nl.sense_os.service.constants.SensePrefs.Main.Ambience;
import nl.sense_os.service.constants.SensePrefs.Main.External;
import nl.sense_os.service.constants.SensePrefs.Main.PhoneState;
import nl.sense_os.service.constants.SensePrefs.Status;
import nl.sense_os.service.constants.SenseUrls;
import nl.sense_os.service.deviceprox.DeviceProximity;
import nl.sense_os.service.external_sensors.NewOBD2DeviceConnector;
import nl.sense_os.service.external_sensors.ZephyrBioHarness;
import nl.sense_os.service.external_sensors.ZephyrHxM;
import nl.sense_os.service.location.LocationSensor;
import nl.sense_os.service.motion.MotionSensor;
import nl.sense_os.service.phonestate.BatterySensor;
import nl.sense_os.service.phonestate.PhoneActivitySensor;
import nl.sense_os.service.phonestate.ProximitySensor;
import nl.sense_os.service.phonestate.SensePhoneState;
import nl.sense_os.service.provider.SNTP;

import org.json.JSONObject;

import android.app.Activity;
import android.app.AlarmManager;
import android.app.Notification;
import android.app.Service;
import android.content.Intent;
import android.content.SharedPreferences;
import android.content.SharedPreferences.Editor;
import android.content.pm.PackageInfo;
import android.os.Build;
import android.os.Handler;
import android.os.HandlerThread;
import android.os.IBinder;
import android.os.Looper;
import android.os.Message;
import android.os.Process;
import android.os.RemoteException;
import android.util.Log;
import android.widget.Toast;

public class SenseService extends Service {

    /**
     * Implementation of the service's AIDL interface.
     */
    private class SenseServiceStub extends ISenseService.Stub {

        private static final String TAG = "SenseServiceStub";

        @Override
        public void changeLogin(final String username, final String password,
                final ISenseServiceCallback callback) throws RemoteException {
            // Log.v(TAG, "Change login");

            // perform login on separate thread and respond via callback
            new Thread() {

                @Override
                public void run() {
                    int result = SenseService.this.changeLogin(username, password);
                    try {
                        callback.onChangeLoginResult(result);
                    } catch (RemoteException e) {
                        Log.e(TAG, "Failed to call back to bound activity after login change: " + e);
                    }
                }
            }.start();
        }

        @Override
        public boolean getPrefBool(String key, boolean defValue) throws RemoteException {
            // Log.v(TAG, "Get preference: " + key);
            SharedPreferences prefs;
            if (key.equals(Status.AMBIENCE) || key.equals(Status.DEV_PROX)
                    || key.equals(Status.EXTERNAL) || key.equals(Status.LOCATION)
                    || key.equals(Status.MAIN) || key.equals(Status.MOTION)
                    || key.equals(Status.PHONESTATE) || key.equals(Status.POPQUIZ)
                    || key.equals(Status.AUTOSTART)) {
                prefs = getSharedPreferences(SensePrefs.STATUS_PREFS, MODE_PRIVATE);
            } else {
                prefs = getSharedPreferences(SensePrefs.MAIN_PREFS, MODE_PRIVATE);
            }

            // return the preference value
            try {
                return prefs.getBoolean(key, defValue);
            } catch (ClassCastException e) {
                return defValue;
            }
        }

        @Override
        public float getPrefFloat(String key, float defValue) throws RemoteException {
            // Log.v(TAG, "Get preference: " + key);
            SharedPreferences prefs = getSharedPreferences(SensePrefs.MAIN_PREFS, MODE_PRIVATE);
            try {
                return prefs.getFloat(key, defValue);
            } catch (ClassCastException e) {
                return defValue;
            }
        }

        @Override
        public int getPrefInt(String key, int defValue) throws RemoteException {
            // Log.v(TAG, "Get preference: " + key);
            SharedPreferences prefs = getSharedPreferences(SensePrefs.MAIN_PREFS, MODE_PRIVATE);
            try {
                return prefs.getInt(key, defValue);
            } catch (ClassCastException e) {
                return defValue;
            }
        }

        @Override
        public long getPrefLong(String key, long defValue) throws RemoteException {
            // Log.v(TAG, "Get preference: " + key);
            SharedPreferences prefs;
            if (key.equals(Auth.SENSOR_LIST_TIME)) {
                prefs = getSharedPreferences(SensePrefs.AUTH_PREFS, MODE_PRIVATE);
            } else {
                prefs = getSharedPreferences(SensePrefs.MAIN_PREFS, MODE_PRIVATE);
            }

            try {
                return prefs.getLong(key, defValue);
            } catch (ClassCastException e) {
                return defValue;
            }
        }

        @Override
        public String getPrefString(String key, String defValue) throws RemoteException {
            // Log.v(TAG, "Get preference: " + key);
            SharedPreferences prefs;
            if (key.equals(Auth.LOGIN_COOKIE) || key.equals(Auth.LOGIN_PASS)
                    || key.equals(Auth.LOGIN_USERNAME) || key.equals(Auth.SENSOR_LIST)
                    || key.equals(Auth.DEVICE_ID) || key.equals(Auth.PHONE_IMEI)
                    || key.equals(Auth.PHONE_TYPE)) {
                prefs = getSharedPreferences(SensePrefs.AUTH_PREFS, MODE_PRIVATE);
            } else {
                // all other preferences
                prefs = getSharedPreferences(SensePrefs.MAIN_PREFS, MODE_PRIVATE);
            }

            // return the preference value
            try {
                return prefs.getString(key, defValue);
            } catch (ClassCastException e) {
                return defValue;
            }
        }

        @Override
        public String getSessionId(String appSecret) throws RemoteException {
            try {
                return SenseApi.getSessionId(SenseService.this, appSecret);
            } catch (IllegalAccessException e) {
                throw new RemoteException();
            }
        }

        @Override
        public void getStatus(ISenseServiceCallback callback) throws RemoteException {
            callback.statusReport(state.getStatusCode());
        }

        public void logout() {
            SenseService.this.logout();
        }

        @Override
        public void register(final String username, final String password, final String name,
                final String surname, final String email, final String mobile,
                final ISenseServiceCallback callback) throws RemoteException {
            // Log.v(TAG, "Register: '" + username + "'");

            // perform registration on separate thread and respond via callback
            new Thread() {

                @Override
                public void run() {
                    int result = SenseService.this.register(username, password, name, surname,
                            email, mobile);
                    try {
                        callback.onRegisterResult(result);
                    } catch (RemoteException e) {
                        Log.e(TAG, "Failed to call back to bound activity after registration: " + e);
                    }
                }
            }.start();
        }

        @Override
        public void setPrefBool(String key, final boolean value) throws RemoteException {
            // Log.v(TAG, "Set preference: '" + key + "': '" + value + "'");

            SharedPreferences prefs;
            if (key.equals(Status.AMBIENCE) || key.equals(Status.DEV_PROX)
                    || key.equals(Status.EXTERNAL) || key.equals(Status.LOCATION)
                    || key.equals(Status.MAIN) || key.equals(Status.MOTION)
                    || key.equals(Status.PHONESTATE) || key.equals(Status.POPQUIZ)
                    || key.equals(Status.AUTOSTART)) {
                prefs = getSharedPreferences(SensePrefs.STATUS_PREFS, MODE_PRIVATE);
            } else {
                prefs = getSharedPreferences(SensePrefs.MAIN_PREFS, MODE_PRIVATE);
            }

            // store value
            boolean stored = prefs.edit().putBoolean(key, value).commit();
            if (stored == false) {
                Log.w(TAG, "Preference '" + key + "' not stored!");
            } else if (key.equals(Advanced.DEV_MODE) && state.isLoggedIn()) {
                logout();
            } else if (key.equals(Advanced.USE_COMMONSENSE)) {
                // login on a separate thread
                new Thread() {
                    public void run() {
                        if (value) {
                            Log.w(TAG, "USE_COMMONSENSE setting changed: try to log in...");
                            login();
                        } else {
                            Log.w(TAG, "USE_COMMONSENSE setting changed: logging out...");
                            logout();
                        }
                    }
                }.start();
            }
        }

        @Override
        public void setPrefFloat(String key, float value) throws RemoteException {
            // Log.v(TAG, "Set preference: " + key + ": \'" + value + "\'");
            SharedPreferences prefs = getSharedPreferences(SensePrefs.MAIN_PREFS, MODE_PRIVATE);

            // store value
            boolean stored = prefs.edit().putFloat(key, value).commit();
            if (stored == false) {
                Log.w(TAG, "Preference " + key + " not stored!");
            }
        }

        @Override
        public void setPrefInt(String key, int value) throws RemoteException {
            // Log.v(TAG, "Set preference: " + key + ": \'" + value + "\'");
            SharedPreferences prefs = getSharedPreferences(SensePrefs.MAIN_PREFS, MODE_PRIVATE);

            // store value
            boolean stored = prefs.edit().putFloat(key, value).commit();
            if (stored == false) {
                Log.w(TAG, "Preference " + key + " not stored!");
            }
        }

        @Override
        public void setPrefLong(String key, long value) throws RemoteException {
            // Log.v(TAG, "Set preference: " + key + ": \'" + value + "\'");
            SharedPreferences prefs;
            if (key.equals(Auth.SENSOR_LIST_TIME)) {
                prefs = getSharedPreferences(SensePrefs.AUTH_PREFS, MODE_PRIVATE);
            } else {
                prefs = getSharedPreferences(SensePrefs.MAIN_PREFS, MODE_PRIVATE);
            }

            // store value
            boolean stored = prefs.edit().putLong(key, value).commit();
            if (stored == false) {
                Log.w(TAG, "Preference " + key + " not stored!");
            }
        }

        @Override
        public void setPrefString(String key, String value) throws RemoteException {
            // Log.v(TAG, "Set preference: " + key + ": \'" + value + "\'");
            SharedPreferences prefs;
            if (key.equals(Auth.LOGIN_COOKIE) || key.equals(Auth.LOGIN_PASS)
                    || key.equals(Auth.LOGIN_USERNAME) || key.equals(Auth.SENSOR_LIST)
                    || key.equals(Auth.DEVICE_ID) || key.equals(Auth.PHONE_IMEI)
                    || key.equals(Auth.PHONE_TYPE)) {
                prefs = getSharedPreferences(SensePrefs.AUTH_PREFS, MODE_PRIVATE);
            } else {
                // all other preferences
                prefs = getSharedPreferences(SensePrefs.MAIN_PREFS, MODE_PRIVATE);
            }

            // store value
            boolean stored = prefs.edit().putString(key, value).commit();
            if (stored == false) {
                Log.w(TAG, "Preference " + key + " not stored!");
            }

            // special check for sync and sample rate changes
            if (key.equals(SensePrefs.Main.SAMPLE_RATE)) {
                onSampleRateChange();
            } else if (key.equals(SensePrefs.Main.SYNC_RATE)) {
                onSyncRateChange();
            }
        }

        @Override
        public void toggleAmbience(boolean active) {
            // Log.v(TAG, "Toggle ambience: " + active);
            SharedPreferences prefs = getSharedPreferences(SensePrefs.STATUS_PREFS, MODE_PRIVATE);
            prefs.edit().putBoolean(Status.AMBIENCE, active).commit();
            SenseService.this.toggleAmbience(active);
        }

        @Override
        public void toggleDeviceProx(boolean active) {
            // Log.v(TAG, "Toggle neighboring devices: " + active);
            SharedPreferences prefs = getSharedPreferences(SensePrefs.STATUS_PREFS, MODE_PRIVATE);
            prefs.edit().putBoolean(Status.DEV_PROX, active).commit();
            SenseService.this.toggleDeviceProx(active);
        }

        @Override
        public void toggleExternalSensors(boolean active) {
            // Log.v(TAG, "Toggle external sensors: " + active);
            SharedPreferences prefs = getSharedPreferences(SensePrefs.STATUS_PREFS, MODE_PRIVATE);
            prefs.edit().putBoolean(Status.EXTERNAL, active).commit();
            SenseService.this.toggleExternalSensors(active);
        }

        @Override
        public void toggleLocation(boolean active) {
            // Log.v(TAG, "Toggle location: " + active);
            SharedPreferences prefs = getSharedPreferences(SensePrefs.STATUS_PREFS, MODE_PRIVATE);
            prefs.edit().putBoolean(Status.LOCATION, active).commit();
            SenseService.this.toggleLocation(active);
        }

        @Override
        public void toggleMain(boolean active) {
            // Log.v(TAG, "Toggle main: " + active);
            SharedPreferences prefs = getSharedPreferences(SensePrefs.STATUS_PREFS, MODE_PRIVATE);
            prefs.edit().putBoolean(Status.MAIN, active).commit();
            SenseService.this.toggleMain(active);
        }

        @Override
        public void toggleMotion(boolean active) {
            // Log.v(TAG, "Toggle motion: " + active);
            SharedPreferences prefs = getSharedPreferences(SensePrefs.STATUS_PREFS, MODE_PRIVATE);
            prefs.edit().putBoolean(Status.MOTION, active).commit();
            SenseService.this.toggleMotion(active);
        }

        @Override
        public void togglePhoneState(boolean active) {
            // Log.v(TAG, "Toggle phone state: " + active);
            SharedPreferences prefs = getSharedPreferences(SensePrefs.STATUS_PREFS, MODE_PRIVATE);
            prefs.edit().putBoolean(Status.PHONESTATE, active).commit();
            SenseService.this.togglePhoneState(active);
        }

        @Override
        public void togglePopQuiz(boolean active) {
            // Log.v(TAG, "Toggle questionnaire: " + active);
            SharedPreferences prefs = getSharedPreferences(SensePrefs.STATUS_PREFS, MODE_PRIVATE);
            prefs.edit().putBoolean(Status.POPQUIZ, active).commit();
            SenseService.this.togglePopQuiz(active);
        }
    }

    private static final String TAG = "Sense Service";

    /**
     * Intent action to force a re-login attempt when the service is started.
     */
    public static final String EXTRA_RELOGIN = "relogin";

    /**
     * Intent action for broadcasts that the service state has changed.
     */
    public final static String ACTION_SERVICE_BROADCAST = "nl.sense_os.service.Broadcast";

    private final ISenseService.Stub binder = new SenseServiceStub();

    private ServiceStateHelper state;

    private BatterySensor batterySensor;
    private DeviceProximity deviceProximity;
    private LightSensor lightSensor;
    private CameraLightSensor cameraLightSensor;
    private TemperatureSensor temperatureSensor;
    private LocationSensor locListener;
    private MotionSensor motionSensor;
    private NoiseSensor noiseSensor;
    private PhoneActivitySensor phoneActivitySensor;
    private PressureSensor pressureSensor;
    private ProximitySensor proximitySensor;
    private SensePhoneState phoneStateListener;
    private ZephyrBioHarness es_bioHarness;
    private ZephyrHxM es_HxM;
    private NewOBD2DeviceConnector es_obd2sensor;

    /**
     * Handler on main application thread to display toasts to the user.
     */
    private final Handler toastHandler = new Handler(Looper.getMainLooper());

    /*
     * fields that handle verification of the sensor IDs for communication with CommonSense
     */
    private final PhoneSensorRegistrator sensorVerifier = new PhoneSensorRegistrator(this);
    private final Timer sensorVerifyTimer = new Timer();
    private TimerTask sensorVerifyTask;
    private long sensorVerifyTimeout = 15000;

    // separate threads for the sensing modules
    private HandlerThread ambienceThread, motionThread, deviceProxThread, extSensorsThread,
            locationThread, phoneStateThread;

    /**
     * Changes login of the Sense service. Removes "private" data of the previous user from the
     * preferences. Can be called by Activities that are bound to the service.
     * 
     * @param username
     *            Username
     * @param password
     *            <PASSWORD>
     * @return 0 if login completed successfully, -2 if login was forbidden, and -1 for any other
     *         errors.
     */
    private int changeLogin(String username, String password) {

        logout();

        // save new username and password in the preferences
        Editor authEditor = getSharedPreferences(SensePrefs.AUTH_PREFS, MODE_PRIVATE).edit();
        authEditor.putString(Auth.LOGIN_USERNAME, username);
        authEditor.putString(Auth.LOGIN_PASS, password);
        authEditor.commit();

        return login();
    }

    private void logout() {
        Log.v(TAG, "Log out...");

        // stop active sensing components
        stopSensorModules();

        // clear cached settings of the previous user (e.g. device id)
        Editor authEditor = getSharedPreferences(SensePrefs.AUTH_PREFS, MODE_PRIVATE).edit();
        authEditor.clear();
        authEditor.commit();

        // log out before changing to a new user
        onLogOut();
    }

    /**
     * Checks if the installed Sense Platform application has an update available, alerting the user
     * via a Toast message.
     */
    private void checkVersion() {
        try {
            PackageInfo packageInfo = getPackageManager().getPackageInfo("nl.sense_os.app", 0);
            String versionName = URLEncoder.encode(packageInfo.versionName);
            Log.i(TAG, "Running Sense App version '" + versionName + "'");

            if (versionName.equals("unstable")) {
                return;
            }

            String url = SenseUrls.VERSION + "?version=" + versionName;
            Map<String, String> response = SenseApi.request(this, url, null, null);
            JSONObject content = new JSONObject(response.get("content"));

            if (content.getString("message").length() > 0) {
                Log.i(TAG, "Newer Sense App version available: " + content.toString());
                showToast(content.getString("message"));
            }

        } catch (Exception e) {
            Log.w(TAG, "Failed to get Sense App version: " + e);
        }
    }

    /**
     * Tries to login using the username and password from the private preferences and updates the
     * {@link #isLoggedIn} status accordingly. Can also be called from Activities that are bound to
     * the service.
     * 
     * @return 0 if login completed successfully, -2 if login was forbidden, and -1 for any other
     *         errors.
     */
    private int login() {
        Log.v(TAG, "Log in...");
{'fraction_non_alphanumeric': 0.10519995196349226, 'fraction_numerical': 0.0009607301549177375, 'mean_word_length': 2.1416283105711913, 'pattern_counts': {'":': 8, '<': 7, '<?xml version=': 0, '>': 9, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '5514368', 'n_tokens_mistral': 11261, 'n_tokens_neox': 10869, 'n_words': 2509}