package nl.sense_os.service.commonsense.senddata; import java.lang.ref.WeakReference; import java.util.Map; import nl.sense_os.service.SenseService; import nl.sense_os.service.R; import nl.sense_os.service.commonsense.SenseApi; import nl.sense_os.service.constants.SensorData.DataPoint; import nl.sense_os.service.storage.LocalStorage; import org.json.JSONArray; import org.json.JSONException; import org.json.JSONObject; import android.content.ContentValues; import android.content.Context; import android.content.Intent; import android.net.Uri; import android.os.Bundle; import android.os.Handler; import android.os.Looper; import android.os.Message; import android.os.PowerManager; import android.os.PowerManager.WakeLock; import android.util.Log; /** * Handler for transmission of a simple JSONObject containing sensor data for one sensor. * * @author Steven Mulder <steven@sense-os.nl> */ public class DataTransmitHandler extends Handler { private static final String TAG = "DataTransmitHandler"; private final WeakReference<Context> ctxRef; private final WeakReference<LocalStorage> storageRef; public DataTransmitHandler(Context context, LocalStorage storage, Looper looper) { super(looper); ctxRef = new WeakReference<Context>(context); storageRef = new WeakReference<LocalStorage>(storage); } private void cleanup(WakeLock wakeLock) { if (null != wakeLock) { wakeLock.release(); wakeLock = null; } } @Override public void handleMessage(Message msg) { // get arguments from message Bundle args = msg.getData(); String name = args.getString("name"); String description = args.getString("description"); String dataType = args.getString("dataType"); String deviceUuid = args.getString("deviceUuid"); String cookie = args.getString("cookie"); JSONObject json = (JSONObject) msg.obj; // check if our references are still valid if (null == ctxRef.get() || null == storageRef.get()) { // parent service has died return; } WakeLock wakeLock = null; try { // make sure the device stays awake while transmitting PowerManager powerMgr = (PowerManager) ctxRef.get().getSystemService( Context.POWER_SERVICE); wakeLock = powerMgr.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, TAG); wakeLock.acquire(); // get sensor URL at CommonSense String url = SenseApi.getSensorUrl(ctxRef.get(), name, description, dataType, deviceUuid); if (url == null) { Log.w(TAG, "No sensor ID for '" + name + "' (yet): data will be retried."); return; } Map<String, String> response = SenseApi.request(ctxRef.get(), url, json, cookie); // Error when sending if ((response == null) || !response.get(SenseApi.RESPONSE_CODE).equals("201")) { // if un-authorized: relogin if ((response != null) && response.get(SenseApi.RESPONSE_CODE).equals("403")) { Log.e(TAG, "You are not logged into sense. In order to use sense service, please login using SwanLake app"); final Intent serviceIntent = new Intent(ctxRef.get().getString( R.string.action_sense_service)); serviceIntent.putExtra(SenseService.EXTRA_RELOGIN, true); ctxRef.get().startService(serviceIntent); } // Show the HTTP response Code if (response != null) { Log.w(TAG, "Failed to send '" + name + "' data. Response code:" + response.get(SenseApi.RESPONSE_CODE) + ", Response content: '" + response.get(SenseApi.RESPONSE_CONTENT) + "'\nData will be retried"); } else { Log.w(TAG, "Failed to send '" + name + "' data.\nData will be retried."); } } // Data sent successfully else { int bytes = json.toString().getBytes().length; Log.i(TAG, "Sent '" + name + "' data! Raw data size: " + bytes + " bytes"); onTransmitSuccess(name, description, json); } } catch (Exception e) { if (null != e.getMessage()) { Log.e(TAG, "Exception sending '" + name + "' data, data will be retried: " + e.getMessage()); } else { Log.e(TAG, "Exception sending '" + name + "' data, data will be retried.", e); } } finally { cleanup(wakeLock); } } private void onTransmitSuccess(String name, String description, JSONObject json) throws JSONException { // new content values with updated transmit state ContentValues values = new ContentValues(); values.put(DataPoint.TRANSMIT_STATE, 1); // select points for this sensor, between the fist and the last time stamp JSONArray dataPoints = json.getJSONArray("data"); String frstTimeStamp = dataPoints.getJSONObject(0).getString("date"); String lastTimeStamp = dataPoints.getJSONObject(dataPoints.length() - 1).getString("date"); long min = Math.round(Double.parseDouble(frstTimeStamp) * 1000); long max = Math.round(Double.parseDouble(lastTimeStamp) * 1000); String where = DataPoint.SENSOR_NAME + "='" + name + "'" + " AND " + DataPoint.SENSOR_DESCRIPTION + "='" + description + "'" + " AND " + DataPoint.TIMESTAMP + ">=" + min + " AND " + DataPoint.TIMESTAMP + " <=" + max; try { Uri contentUri = Uri.parse("content://" + ctxRef.get().getString(R.string.local_storage_authority) + DataPoint.CONTENT_URI_PATH); int updated = storageRef.get().update(contentUri, values, where, null); if (updated == dataPoints.length()) { // Log.v(TAG, "Updated all " + updated + " rows in the local storage"); } else { Log.w(TAG, "Wrong number of local storage points updated! " + updated + " vs. " + dataPoints.length()); } } catch (IllegalArgumentException e) { Log.e(TAG, "Error updating points in Local Storage!", e); } } }