package nl.sense_os.service.commonsense.senddata;
import java.io.DataOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.lang.ref.WeakReference;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.Locale;
import java.util.zip.GZIPOutputStream;
import javax.net.ssl.HttpsURLConnection;
import nl.sense_os.service.R;
import nl.sense_os.service.constants.SensePrefs;
import nl.sense_os.service.constants.SenseUrls;
import nl.sense_os.service.constants.SensePrefs.Main.Advanced;
import nl.sense_os.service.constants.SensorData.DataPoint;
import nl.sense_os.service.storage.LocalStorage;
import org.apache.http.conn.ssl.SSLSocketFactory;
import org.json.JSONArray;
import org.json.JSONObject;
import android.content.ContentValues;
import android.content.Context;
import android.content.SharedPreferences;
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 file as sensor data to CommonSense.
*
* @author Steven Mulder <steven@sense-os.nl>
*/
public class FileTransmitHandler extends Handler {
private static final String TAG = "FileTransmitHandler";
private final WeakReference<Context> ctxRef;
private final WeakReference<LocalStorage> storageRef;
public FileTransmitHandler(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 message) {
// get arguments from message
Bundle args = message.getData();
String name = args.getString("name");
String cookie = args.getString("cookie");
JSONObject json = (JSONObject) message.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();
SharedPreferences sMainPrefs = ctxRef.get().getSharedPreferences(SensePrefs.MAIN_PREFS, Context.MODE_PRIVATE);
boolean devMode = sMainPrefs.getBoolean(Advanced.DEV_MODE, false);
String urlStr = devMode ? SenseUrls.DATAPROCESSOR_FILE_DEV : SenseUrls.DATAPROCESSOR_FILE;
// submit each file separately
JSONArray data = json.getJSONArray("data");
for (int i = 0; i < data.length(); i++) {
JSONObject object = (JSONObject) data.get(i);
String fileName = (String) object.get("value");
HttpURLConnection conn = null;
DataOutputStream dos = null;
int bytesRead, bytesAvailable, bufferSize;
byte[] buffer;
int maxBufferSize = 1 * 1024 * 1024;
// ------------------ CLIENT REQUEST
File file = new File(fileName);
FileInputStream fileInputStream = new FileInputStream(file);
// get the filename without the directory
String strippedFile = fileName.substring(fileName.lastIndexOf("/"));
urlStr += strippedFile;
// open a URL connection to the Servlet
URL url = new URL(urlStr);
if ("https".equals(url.getProtocol().toLowerCase(Locale.ENGLISH))) {
HttpsURLConnection https = (HttpsURLConnection) url.openConnection();
https.setHostnameVerifier(SSLSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER);
conn = https;
} else {
conn = (HttpURLConnection) url.openConnection();
}
// Allow Inputs
conn.setDoInput(true);
// Allow Outputs
conn.setDoOutput(true);
// Don't use a cached copy.
conn.setUseCaches(false);
// Use a post method.
conn.setRequestMethod("POST");
conn.setRequestProperty("Cookie", cookie);
conn.setRequestProperty("Connection", "Keep-Alive");
conn.setRequestProperty("Transfer-Encoding", "chunked");
conn.setRequestProperty("Content-Encoding", "gzip");
dos = new DataOutputStream(conn.getOutputStream());
GZIPOutputStream zipStream = new GZIPOutputStream(conn.getOutputStream());
dos = new DataOutputStream(zipStream);
// create a buffer of maximum size
bytesAvailable = fileInputStream.available();
bufferSize = Math.min(bytesAvailable, maxBufferSize);
buffer = new byte[bufferSize];
// read file and write it into form...
bytesRead = fileInputStream.read(buffer, 0, bufferSize);
while (bytesRead > 0) {
dos.write(buffer, 0, bufferSize);
bytesAvailable = fileInputStream.available();
bufferSize = Math.min(bytesAvailable, maxBufferSize);
bytesRead = fileInputStream.read(buffer, 0, bufferSize);
}
// send multipart form data necesssary after file data...
// close streams
fileInputStream.close();
dos.flush();
dos.close();
if (conn.getResponseCode() != 200) {
Log.e(TAG, "Failed to send '" + name
+ "' value file. Data lost. Response code:" + conn.getResponseCode());
} else {
Log.i(TAG, "Sent '" + name + "' sensor value file OK!");
String date = (String) object.get("date");
onTransmitSuccess(name, date);
// remove temp file
if(file.exists())
{
try
{
Log.d(TAG, "Removing File: "+fileName);
file.delete();
}
catch(Exception e)
{
Log.e(TAG, "Error removing temporary file", e);
}
}
}
}
} catch (Exception e) {
Log.e(TAG, "Sending '" + name + "' sensor file failed. Data lost.", e);
} finally {
cleanup(wakeLock);
}
}
private void onTransmitSuccess(String sensorName, String timeInSecs) {
// new content values with updated transmit state
ContentValues values = new ContentValues();
values.put(DataPoint.TRANSMIT_STATE, 1);
long timestamp = Math.round(Double.parseDouble(timeInSecs) * 1000);
String where = DataPoint.SENSOR_NAME + "='" + sensorName + "'" + " AND "
+ DataPoint.TIMESTAMP + "=" + timestamp;
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 != 1) {
Log.w(TAG,
"Failed to update the local storage after a file was successfully sent to CommonSense!");
}
} catch (IllegalArgumentException e) {
Log.e(TAG, "Error updating points in Local Storage!", e);
}
}
}