Android Open Source - airprobe Store And Forward Service






From Project

Back to project page airprobe.

License

The source code is released under:

GNU General Public License

If you think the Android project airprobe listed in this page is inappropriate, such as containing malicious code/tools or violating the copyright, please email info at java2s dot com, thanks.

Java Source Code

/**
 * AirProbe// w  w  w.  j a v  a  2 s .c  o m
 * Air quality application for Android, developed as part of 
 * EveryAware project (<http://www.everyaware.eu>).
 *
 * Copyright (C) 2014 CSP Innovazione nelle ICT. All rights reserved.
 * 
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 * 
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 * 
 * For any inquiry please write to <devel@csp.it>
 * 
 * CONTRIBUTORS
 * 
 * This program was made with the contribution of:
 *   Fabio Saracino <fabio.saracino@csp.it>
 *   Patrick Facco <patrick.facco@csp.it>
 * 
 * 
 * SOURCE CODE
 * 
 *  The source code of this program is available at
 *  <https://github.com/CSPICT/airprobe>
 */

package org.csp.everyaware.internet;

import java.io.BufferedOutputStream;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.ByteArrayOutputStream;
import java.io.DataOutputStream;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.PrintStream;
import java.net.URI;
import java.net.URL;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Locale;
import java.util.zip.GZIPOutputStream;

import javax.net.ssl.HttpsURLConnection;

import org.apache.http.Header;
import org.apache.http.HttpResponse;
import org.apache.http.NameValuePair;
import org.apache.http.ParseException;
import org.apache.http.ProtocolException;
import org.apache.http.StatusLine;
import org.apache.http.client.ClientProtocolException;
import org.apache.http.client.RedirectHandler;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.conn.HttpHostConnectException;
import org.apache.http.entity.ByteArrayEntity;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.message.BasicNameValuePair;
import org.apache.http.protocol.HttpContext;
import org.apache.http.util.EntityUtils;
import org.csp.everyaware.Constants;
import org.csp.everyaware.R;
import org.csp.everyaware.Start;
import org.csp.everyaware.Utils;
import org.csp.everyaware.db.DbManager;
import org.csp.everyaware.db.Record;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;

import android.app.Activity;
import android.app.Service;
import android.content.Intent;
import android.os.AsyncTask;
import android.os.Handler;
import android.os.IBinder;
import android.util.Log;

public class StoreAndForwardService extends Service
{
  private Handler mHandler;
  private boolean mConnectivityOn;
  private DbManager mDbManager;
  private List<Record>mToSendRecords;
  private List<Record>mAllLoadedRecords;
  private PostDataThread mPostDataThread;
  
  @Override
  public void onCreate() 
  {
    Log.d("StoreAndForwardService", "***************************** onCreate() ***************************");
  
    mHandler = new Handler();
    mDbManager = DbManager.getInstance(getApplicationContext());
    mPostDataThread = new PostDataThread();
  }
  
  @Override
  public int onStartCommand(Intent intent, int flags, int startId) 
  {
    Log.d("StoreAndForwardService", "*************************** onStartCommand() ***********************");
    
    mHandler.postDelayed(mSendRecordsRunnable, Utils.getStoreForwInterval(getApplicationContext()));
    
    return super.onStartCommand(intent, flags, startId);    
  }
  
  public void onStart(Intent intent, int startId) 
  {
    Log.d("StoreAndForwardService", "****************************** onStart() ***************************");
    super.onStart(intent, startId);     
  }
  
  @Override
  public void onDestroy() 
  {
    Log.d("StoreAndForwardService", "***************************** onDestroy() *************************");  
    
    mHandler.removeCallbacks(mSendRecordsRunnable);
  }
  
  @Override
  public IBinder onBind(Intent arg0) 
  {
    return null;
  }

  /************************* RUNNABLE CHE INVIA RECORDS AL SERVER ****************************************/
  
  private Runnable mSendRecordsRunnable = new Runnable()
  {    
    @Override
    public void run() 
    {
      Log.d("SendRecordsRunnable", "run() *******************************");

      if((mToSendRecords != null)&&(mToSendRecords.size() > 0))
      {
        Log.d("SendRecordsRunnable", "run()--> mToSendsRecords still contains data. This runnable will be recalled after N secs");
        mHandler.postDelayed(this, Constants.storeForwHistFreqs);
      }
      else
      {  
        //read network type index on which upload data is allowed: 0 - only wifi; 1 - both wifi and mobile
        int networkTypeIndex = Utils.getUploadNetworkTypeIndex(getApplicationContext());
        
        //1 - is internet connection available? 
        boolean[] connectivity = Utils.haveNetworkConnection(getApplicationContext());
        
        //if user wants to upload only on wifi networks, connectivity[0] (network connectivity) must be true
        if(networkTypeIndex == 0)
        {
          if(connectivity[0])
            mConnectivityOn = true;
          else
            mConnectivityOn = false;
        }
        else //if user wants to upload both on wifi/mobile networks
          mConnectivityOn = connectivity[0] || connectivity[1];
        
        if(mConnectivityOn)
        {
          sendBroadcast(new Intent(Constants.INTERNET_ON));
          Utils.uploadOn = Constants.INTERNET_ON_INT;
        }
        else
        {
          sendBroadcast(new Intent(Constants.INTERNET_OFF));
          Utils.uploadOn = Constants.INTERNET_OFF_INT;
        }
        
        Log.d("SendRecordsRunnable", "run()--> Internet connection on: " +mConnectivityOn);
        
        if(mConnectivityOn)
        {        
          if(mDbManager == null)
            mDbManager = DbManager.getInstance(getApplicationContext());
          
          //if store'n'forward interval is 1 sec, load only records marked with actual session Id
          if(Utils.getStoreForwInterval(getApplicationContext()) == 1000)
            //2 - load from DB the N oldest record not yet sent to server
            mAllLoadedRecords = mDbManager.loadOlderNotUploadedRecords(Constants.REC_TO_UPLOAD_MAX_NUM, true);
          else
          {
            //2 - load from DB the N oldest record not yet sent to server
            if(Utils.historyDownloadMode)
              mAllLoadedRecords = mDbManager.loadOlderNotUploadedRecords(Constants.REC_TO_UPLOAD_MAX_NUM*4, false);
            else
              mAllLoadedRecords = mDbManager.loadOlderNotUploadedRecords(Constants.REC_TO_UPLOAD_MAX_NUM, false);
          }
                    
          //if there are records to send, send them, but, from ap 1.4, check if they are all of the 
          //same semantic session seed (that can be an empty string if the are recorded out of a semantic
          //window)
          //only record in the mToSendRecords array will be sent to the server
          if((mAllLoadedRecords != null)&&(mAllLoadedRecords.size() > 0))
          {
             mToSendRecords = new ArrayList<Record>();
             String semanticSessionSeed = "";
             int k = 0;
             Record temp = null;
             boolean stop = false;
             
             while((k < mAllLoadedRecords.size())&&(!stop))
             {
               temp = mAllLoadedRecords.get(k);
              
               //initialize value with semantic session seed from the first record
               if(k == 0)
                 semanticSessionSeed = temp.mSemanticSessionSeed;
               
               //if actual record contains the same session seed of the first record,
               //add to array of record to be sent
               if(semanticSessionSeed.equals(temp.mSemanticSessionSeed))
                 mToSendRecords.add(temp);
               else //else stop! 
                 stop = true;
               
               k++;
             }
            
             Log.d("SendRecordsRunnable", "run()--> # all loaded records: "+mAllLoadedRecords.size()+" - # to send records: "+mToSendRecords.size());
             /*** LOGGED USER ACCESS TOKEN ***/
             //check if airprobe has been activated
             if(Utils.getAccountActivationState(getApplicationContext()))
             {
              //check if access token is available and valid. If it is expired (creation time + expires in is > actual time), ask for a new one
              if(checkIfAccessTokenIsValid())
              {
                Log.d("SendRecordsRunnable", "run()--> access token is still valid");
    
                try
                {
                  //3 - send data to server in a gzip http compression format  
                  sendBroadcast(new Intent(Constants.UPLOAD_ON)); //send msg to UI thread
                  Utils.uploadOn = Constants.UPLOAD_ON_INT;
                  
                  mPostDataThread = new PostDataThread();
                  mPostDataThread.start();    
                } 
                    catch (Exception e) 
                    {
                  e.printStackTrace();
                }
                finally
                {
                  //5 - next runnable call is scheduled
                  if(!Utils.historyDownloadMode)
                    mHandler.postDelayed(this, Utils.getStoreForwInterval(getApplicationContext()));
                  else
                    mHandler.postDelayed(this, Constants.storeForwHistFreqs);
                }
              }
              //if AirProbe has been activated but access token is exipired, require new access token and send data
              else
              {
                Log.d("SendRecordsRunnable", "run()--> access token expired - ask for new one");
                new RefreshTokenTask().execute();
              }
             }
             /*** CLIENT ACCESS TOKEN ***/
             //if AirProbe has not been activated, check if anonymous client access token is valid and use it to send data
             else
             {
              //check if access token for client is available and valid. If it is expired (creation time + expires in is > actual time), ask for a new one
               if(checkIfAccessTokenIsValidForClient())
               {
                 Log.d("SendRecordsRunnable", "run()--> access token FOR CLIENT is still valid");
                 
                 try
                 {
                  //3 - send data to server in a gzip http compression format  
                  sendBroadcast(new Intent(Constants.UPLOAD_ON)); //send msg to UI thread
                  Utils.uploadOn = Constants.UPLOAD_ON_INT;
                    
                  mPostDataThread = new PostDataThread();
                  mPostDataThread.start();    
                 } 
                   catch (Exception e) 
                   {
                  e.printStackTrace();
                 }
                 finally
                 {
                  //5 - next runnable call is scheduled
                  if(!Utils.historyDownloadMode)
                    mHandler.postDelayed(this, Utils.getStoreForwInterval(getApplicationContext()));
                  else
                    mHandler.postDelayed(this, Constants.storeForwHistFreqs);
                 }
               }
               //if AirProbe has not been activated and access token for client is exipired, require new access token for client and send data
               else
               {
                Log.d("SendRecordsRunnable", "run()--> access token for client expired - ask for new one");
                new RefreshTokenTaskForClient().execute();
               }
             }
          }
          else
          {
            sendBroadcast(new Intent(Constants.FINISHED_UPLOAD));
            Utils.uploadOn = Constants.INTERNET_ON_INT;
            
            Log.d("SendRecordsRunnable", "run()--> No records to send");
            
            //next runnable call is scheduled
            if(!Utils.historyDownloadMode)
              mHandler.postDelayed(this, Utils.getStoreForwInterval(getApplicationContext()));
            else
              mHandler.postDelayed(this, Constants.storeForwHistFreqs);
          }
        }
        else
        {
          Log.d("SendRecordsRunnable", "run()--> No internet connectivity");
          //next runnable call is scheduled
          if(!Utils.historyDownloadMode)
            mHandler.postDelayed(this, Utils.getStoreForwInterval(getApplicationContext()));
          else
            mHandler.postDelayed(this, Constants.storeForwHistFreqs);
        }
      }
    }  
  };
  
  /********************** CALLS postData() FUNCTION *************************************************/
  
  private class PostDataThread extends Thread
  {
    @Override
    public void run()
    {
      int statusCode = -1;
      //int statusCodeTags = -1;
      
      try 
      {
        Thread.currentThread().sleep(0); //helpful for icon on/off sync
        
        if(Utils.report_url == null)
        {
          getServer();
        
          if(Utils.report_url != null)
          {
            //statusCode = postData();
            statusCode = postSecureData();
            postSecureTags();
            //postTags();        
          }
        }
        else
        {
          //statusCode = postData();      
          statusCode = postSecureData();
          //postTags();
          postSecureTags();
        }
      } 
      catch(InterruptedException e)
      {
        e.printStackTrace();
      }
      //this exception is invoked when internet connection is up but traffic is not allowed
      //(for example, for networks that needs login but user is not logged)
      catch (HttpHostConnectException e) 
      {
        e.printStackTrace();
      } 
      catch (IllegalArgumentException e) 
      {
        e.printStackTrace();
      } 
      catch (ClientProtocolException e) 
      {
        e.printStackTrace();
      } 
      catch (IOException e) 
      {
        e.printStackTrace();
      }
      
      Log.d("PostDataThread", "run()--> status code: " +statusCode);
      
                /**********************/    
        
      //4 - if server response is HTTP 200 OK, mark sent records on db with timestamp
      //    at the moment of data post
      if(statusCode == Constants.STATUS_OK)
      {
        long uploadSysTs = System.currentTimeMillis(); //actual system timestamp    
        
        Log.d("PostDataThread", "run()--> # records to update with upload sys ts: "+mToSendRecords.size());
        int size = mDbManager.updateUploadedRecord(mToSendRecords, uploadSysTs);
        
        //increment number of uploaded records stored on DB by 'size' quantity
        Utils.incrUploadedRecCount(getApplicationContext(), size);
      }  
      //4b - if server response is HTTP 401 OK, access_token is expired and before sending again data
      //     I need to require a new access token to server
      if(statusCode == Constants.STATUS_UNAUTHORIZED)
      {
        Log.d("PostDataThread", "run()--> STATUS_UNAUTHORIZED"); 
        //do nothing in this case. This call to send record runnable isn't useful
      }
      
      //5 - cleaning of array to free memory
      if(mAllLoadedRecords != null)
        mAllLoadedRecords.clear();
      if(mToSendRecords != null)
        mToSendRecords.clear();    
      
      //send msg to UI thread
      sendBroadcast(new Intent(Constants.UPLOAD_OFF));
      Utils.uploadOn = Constants.INTERNET_ON_INT;
      
      synchronized(Start.class)
          {
            mPostDataThread = null;
          }
    }
  }
  
  /*************** SCRIVE FILE DI TESTO CONTENENTE OGGETTI JSON *****************************************/
  
  public void writeTextFile(File txtFile) throws IOException
  {
    BufferedWriter bW = null;
    
    try 
    {
      bW = new BufferedWriter(new FileWriter(txtFile));
      bW.write("[");        
      for(int i = 0; i < mToSendRecords.size(); i++)
      {
        bW.write(mToSendRecords.get(i).toJson().toString());        
        if(i != mToSendRecords.size()-1)  
          bW.write(",");            
        bW.write("\n");
      }        
      bW.write("]");
    } 
    finally
    {
      try
      {
        if(bW != null)
          bW.close();
      }
      catch(IOException e)
      {
        e.printStackTrace();
      }
    }
  }

  /***************** METODO CHE COMPRIME UN FILE DI TESTO IN UNO ZIP **********************************/
  /*
  private void compressFile(String inputFilename, String outputFilename) throws IOException
  {
    // Create a buffer for reading the files
    byte[] buf = new byte[1024];

    // Create the ZIP file
    ZipOutputStream out = new ZipOutputStream(new FileOutputStream(outputFilename));

    // Compress the files
    FileInputStream in = new FileInputStream(inputFilename);

    int index = inputFilename.lastIndexOf(File.separator);
    inputFilename = inputFilename.substring(index+1);
        
    // Add ZIP entry to output stream.
    out.putNextEntry(new ZipEntry(inputFilename));

    // Transfer bytes from the file to the ZIP file
    int len;
    while ((len = in.read(buf)) > 0) 
    {
      out.write(buf, 0, len);
    }

    // Complete the entry
    out.closeEntry();
    in.close();

    // Complete the ZIP file
    out.close();
  }*/
  
  /************************ NEW TOKEN REQUEST *******************************************/
  
  private class RefreshTokenTask extends AsyncTask<Void, Void, Boolean>
  {
    private boolean success = false;
    
    @Override
    protected Boolean doInBackground(Void... params) 
    {  
      try 
      {  
        //doHttpPost();
        
        doSecureHttpPost();
        
      } 
      catch (ClientProtocolException e) 
      {
        e.printStackTrace();
      } 
      catch (IOException e) 
      {
          e.printStackTrace();
        }
        catch (JSONException e1) 
      {
        e1.printStackTrace();
      }
      catch(OutOfMemoryError e)
      {
          e.printStackTrace();
      }     
      catch (IllegalArgumentException e) 
      {
        e.printStackTrace();
      } 
      return success;
    }  
    
    //if access token needed to be updated, the send of actual set of records is performed here
    @Override
    protected void onPostExecute(Boolean success)
    {
      Log.d("RefreshTokenTask", "onPostExecute()");
    
      try
      {
        if(success)
        {
          //3 - send data to server in a gzip http compression format  
          sendBroadcast(new Intent(Constants.UPLOAD_ON)); //send msg to UI thread
          Utils.uploadOn = Constants.UPLOAD_ON_INT;
          
          mPostDataThread = new PostDataThread();
          mPostDataThread.start();    
        }
        else
        {
          if(mAllLoadedRecords != null)
            mAllLoadedRecords.clear();
          //cleaning of array to free memory
          if(mToSendRecords != null)
            mToSendRecords.clear();    
          
          //send msg to UI thread
          sendBroadcast(new Intent(Constants.UPLOAD_OFF));
          Utils.uploadOn = Constants.INTERNET_ON_INT;
        }
      } 
          catch (Exception e) 
          {
        e.printStackTrace();
      }
      finally
      {
        //5 - next runnable call is scheduled
        if(!Utils.historyDownloadMode)
          mHandler.postDelayed(mSendRecordsRunnable, Utils.getStoreForwInterval(getApplicationContext()));
        else
          mHandler.postDelayed(mSendRecordsRunnable, Constants.storeForwHistFreqs);
      }
    }
    
    //makes a not secure (http://) post with hidden parameters to request the couple <access_token, refresh_token>
    //not used but do not delete it
    private void doHttpPost() throws ClientProtocolException, IOException, JSONException, OutOfMemoryError, IllegalArgumentException
    {
      int statusCode = -1;
      
      //http post to hide sent params  
      DefaultHttpClient httpClient = new DefaultHttpClient();
      HttpPost httpPost = new HttpPost(Constants.REFRESH_TOKEN_URL);
      
        // Add  data
        List<NameValuePair> nameValuePairs = new ArrayList<NameValuePair>(2);
        nameValuePairs.add(new BasicNameValuePair("grant_type", "refresh_token"));
        nameValuePairs.add(new BasicNameValuePair("client_id", "airprobe_android_client"));
        nameValuePairs.add(new BasicNameValuePair("client_secret", Constants.SECRET_KEY));
        nameValuePairs.add(new BasicNameValuePair("refresh_token", Utils.getRefreshToken(getApplicationContext())));      
        httpPost.setEntity(new UrlEncodedFormEntity(nameValuePairs));

        // Execute HTTP Post Request
        HttpResponse response = httpClient.execute(httpPost);
            
      //copio la entity response su output stream, ottenendo una stringa
      ByteArrayOutputStream out = new ByteArrayOutputStream();
        response.getEntity().writeTo(out);
        String responseString = out.toString();
        Log.d("RefreshTokenTask", "doHttpPost()--> "+responseString);
          
      statusCode = response.getStatusLine().getStatusCode();        
        
      //se la risposta dal server ? 'HTTP 200 OK'
      if(statusCode == Constants.STATUS_OK)
      {
          if (response.getEntity() != null) 
          {  
            JSONObject json = new JSONObject(responseString);
            String accessToken = json.getString("access_token");
            String refreshToken = json.getString("refresh_token");
            String tokenType = json.getString("token_type");              
            int expiresIn = json.getInt("expires_in");
                
            Log.d("RefreshTokenTask", "doHttpPost()--> "+accessToken+" "+refreshToken+" "+tokenType+" "+expiresIn);  
                
            //save account credentials in shared preferences
            Utils.setCredentialsData(getApplicationContext(), accessToken, refreshToken, tokenType, expiresIn, System.currentTimeMillis() / 1000);
                
            success = true;
          }                 
      }
      else
      {
          if (response.getEntity() != null) 
          {  
            JSONObject json = new JSONObject(responseString);
            String error = json.getString("error");
            String errorDescr = json.getString("error_description");
                
            Log.d("RefreshTokenTask", "doHttpPost()--> "+error+" "+errorDescr);
                
            success = false;
          }     
      }      
    }
    
    //makes a secure (https://) http post to request the couple <access_token, refresh_token>
    private void doSecureHttpPost() throws ClientProtocolException, IOException, JSONException, OutOfMemoryError, IllegalArgumentException
    {      
      String urlString = Constants.REFRESH_TOKEN_URL;
      String urlParameters = "?grant_type=refresh_token&client_id=airprobe_android_client&client_secret="+Constants.SECRET_KEY+"&refresh_token="+Utils.getRefreshToken(getApplicationContext());
      
      URL url = new URL(urlString+urlParameters);
      //URL url = new URL(Constants.REFRESH_TOKEN_URL);
        HttpsURLConnection con = (HttpsURLConnection)url.openConnection();
        
        con.setRequestMethod("POST");
        con.setUseCaches(false);
        con.setDoInput(true);
      con.setDoOutput(true);
      
      int responseCode = con.getResponseCode();
      //Log.d("RefreshTokenTask", "doSecureHttpPost()--> Sending 'POST' request to URL : " + url);
      //Log.d("RefreshTokenTask", "doSecureHttpPost()--> Post parameters : " + urlParameters);
      Log.d("RefreshTokenTask", "doSecureHttpPost()--> Response Code : " + responseCode);

        String responseString = getStringFromInputStream(con.getInputStream());
        Log.d("RefreshTokenTask", "doSecureHttpPost()--> "+responseString);
        
      //se la risposta dal server ? 'HTTP 200 OK'
      if(responseCode == Constants.STATUS_OK)
      {
        JSONObject json = new JSONObject(responseString);
        String accessToken = json.getString("access_token");
        String refreshToken = json.getString("refresh_token");
        String tokenType = json.getString("token_type");              
        int expiresIn = json.getInt("expires_in");
                
        Log.d("RefreshTokenTask", "doSecureHttpPost()--> "+accessToken+" "+refreshToken+" "+tokenType+" "+expiresIn);  
                
        //save account credentials in shared preferences
        Utils.setCredentialsData(getApplicationContext(), accessToken, refreshToken, tokenType, expiresIn, System.currentTimeMillis() / 1000);
                
        success = true;                           
      }
      else
      { 
        JSONObject json = new JSONObject(responseString);
        String error = json.getString("error");
        String errorDescr = json.getString("error_description");
                
        Log.d("RefreshTokenTask", "doSecureHttpPost()--> "+error+" "+errorDescr);
                
        success = false;     
      }    
    }
    
    private String getStringFromInputStream(InputStream is)
    { 
      BufferedReader br = null;
      StringBuilder sb = new StringBuilder();
   
      String line;
      try 
      {
        br = new BufferedReader(new InputStreamReader(is));
        while ((line = br.readLine()) != null) 
        {
          sb.append(line);
        }
   
      } 
      catch (IOException e) 
      {
        e.printStackTrace();
      } 
      finally 
      {
        if (br != null) 
        {
          try 
          {
            br.close();
          } 
          catch (IOException e) 
          {
            e.printStackTrace();
          }
        }
      }
   
      return sb.toString();   
    }
  }
  
  private class RefreshTokenTaskForClient extends AsyncTask<Void, Void, Boolean>
  {
    private boolean success = false;
    
    @Override
    protected Boolean doInBackground(Void... params) 
    {  
      try 
      {  
        doSecureHttpPostForClient();          
      } 
      catch (ClientProtocolException e) 
      {
        e.printStackTrace();
      } 
      catch (IOException e) 
      {
          e.printStackTrace();
        }
        catch (JSONException e1) 
      {
        e1.printStackTrace();
      }
      catch(OutOfMemoryError e)
      {
          e.printStackTrace();
      }     
      catch (IllegalArgumentException e) 
      {
        e.printStackTrace();
      } 
      return success;
    }  
    
    //if access token needed to be updated, the send of actual set of records is performed here
    @Override
    protected void onPostExecute(Boolean success)
    {
      Log.d("RefreshTokenTaskForClient", "onPostExecute()");
    
      try
      {
        if(success)
        {
          //3 - send data to server in a gzip http compression format  
          sendBroadcast(new Intent(Constants.UPLOAD_ON)); //send msg to UI thread
          Utils.uploadOn = Constants.UPLOAD_ON_INT;
          
          mPostDataThread = new PostDataThread();
          mPostDataThread.start();
        }
        else
        {
          if(mAllLoadedRecords != null)
            mAllLoadedRecords.clear();
          //cleaning of array to free memory
          if(mToSendRecords != null)
            mToSendRecords.clear();    
          
          //send msg to UI thread
          sendBroadcast(new Intent(Constants.UPLOAD_OFF));
          Utils.uploadOn = Constants.INTERNET_ON_INT;
        }
      } 
          catch (Exception e) 
          {
        e.printStackTrace();
      }
      finally
      {
        //5 - next runnable call is scheduled
        if(!Utils.historyDownloadMode)
          mHandler.postDelayed(mSendRecordsRunnable, Utils.getStoreForwInterval(getApplicationContext()));
        else
          mHandler.postDelayed(mSendRecordsRunnable, Constants.storeForwHistFreqs);
      }
    }    
  
    //makes a secure (https://) http post to request the couple <access_token, refresh_token>
    private void doSecureHttpPostForClient() throws ClientProtocolException, IOException, JSONException, OutOfMemoryError, IllegalArgumentException
    {      
      String urlString = Constants.REFRESH_TOKEN_URL;
      String urlParameters = "?grant_type=client_credentials&client_id=airprobe_android_client&client_secret="+Constants.SECRET_KEY;
      
      URL url = new URL(urlString+urlParameters);
        HttpsURLConnection con = (HttpsURLConnection)url.openConnection();
        
        con.setRequestMethod("POST");
        con.setUseCaches(false);
        con.setDoInput(true);
      con.setDoOutput(true);
      
      int responseCode = con.getResponseCode();
      //Log.d("RefreshTokenTaskForClient", "doSecureHttpPostForClient()--> Sending 'POST' request to URL : " + url);
      //Log.d("RefreshTokenTaskForClient", "doSecureHttpPostForClient()--> Post parameters : " + urlParameters);
      Log.d("RefreshTokenTaskForClient", "doSecureHttpPostForClient()--> Response Code : " + responseCode);
  
        String responseString = getStringFromInputStream(con.getInputStream());
        Log.d("RefreshTokenTaskForClient", "doSecureHttpPostForClient()--> "+responseString);
        
      //se la risposta dal server ? 'HTTP 200 OK'
      if(responseCode == Constants.STATUS_OK)
      {
        JSONObject json = new JSONObject(responseString);
        String accessToken = json.getString("access_token");
        //String refreshToken = json.getString("refresh_token");
        String tokenType = json.getString("token_type");              
        int expiresIn = json.getInt("expires_in");
                
        Log.d("RefreshTokenTaskForClient", "doSecureHttpPostForClient()--> "+accessToken+" "+tokenType+" "+expiresIn);  
                
        //save account credentials in shared preferences
        Utils.setCredentialsDataForClient(getApplicationContext(), accessToken, tokenType, expiresIn, System.currentTimeMillis() / 1000);
                
        success = true;                           
      }
      else
      { 
        JSONObject json = new JSONObject(responseString);
        String error = json.getString("error");
        String errorDescr = json.getString("error_description");
                
        Log.d("RefreshTokenTaskForClient", "doSecureHttpPostForClient()--> "+error+" "+errorDescr);
                
        success = false;     
      }    
    }
    
    private String getStringFromInputStream(InputStream is)
    { 
      BufferedReader br = null;
      StringBuilder sb = new StringBuilder();
   
      String line;
      try 
      {
        br = new BufferedReader(new InputStreamReader(is));
        while ((line = br.readLine()) != null) 
        {
          sb.append(line);
        }
   
      } 
      catch (IOException e) 
      {
        e.printStackTrace();
      } 
      finally 
      {
        if (br != null) 
        {
          try 
          {
            br.close();
          } 
          catch (IOException e) 
          {
            e.printStackTrace();
          }
        }
      }
   
      return sb.toString();   
    }
  }
  
  /***************** GET ENDPOINT ADDRESS FROM REDIRECT SERVER **************************/
  
  public void getServer() throws IllegalArgumentException, ClientProtocolException, 
  HttpHostConnectException, IOException
  {
    Log.d("StoreAndForwardService", "getServer()");
    
    DefaultHttpClient httpClient = new DefaultHttpClient();    
      HttpPost httpPost = new HttpPost(Constants.REDIRECT_ADDR);
      httpClient.setRedirectHandler(new RedirectHandler()
      {
      @Override
      public URI getLocationURI(HttpResponse response, HttpContext context) throws ProtocolException 
      {
        Log.d("StoreAndForwardService", "getServer() - getLocationURI()");
        return null;
      }
  
      @Override
      public boolean isRedirectRequested(HttpResponse response, HttpContext context) throws ParseException 
      {
        String responseBody = null;
        
        try 
        {
          responseBody = EntityUtils.toString(response.getEntity());
        } 
        catch (IOException e) 
        {
          e.printStackTrace();
        }
        catch(OutOfMemoryError e)
        {
          e.printStackTrace();
        }
        
        if(responseBody != null)
        {
          Log.d("StoreAndForwardService", "getServer() - isRedirectRequested()--> status line: " +response.getStatusLine());
          
          if(response.getStatusLine().getStatusCode() == 302)
          {  
            Header[] locHeader = response.getHeaders("Location");
            if((locHeader != null)&&(locHeader.length > 0))
            {
              Utils.report_url = locHeader[0].getValue();
              Log.d("StoreAndForwardService", "getServer() - isRedirectRequested()--> report url: " +Utils.report_url);              
            }
            
            Header[] countryHeader = response.getHeaders("Country");
            if((countryHeader != null)&&(countryHeader.length > 0))
            {
              Utils.report_country = countryHeader[0].getValue();
              Log.d("StoreAndForwardService", "getServer() - isRedirectRequested()--> report country: " +Utils.report_country);
            }
          }
          else
            Log.d("StoreAndForwardService", "getServer() - isRedirectRequested()--> redirect server response is WRONG!");
        }
        else
          Log.d("StoreAndForwardService", "getServer() - isRedirectRequested()--> response body from redirect server is NULL!");
        
        return false;
      }
    });
      
      httpClient.execute(httpPost);
  }
  
  /********************** SEND DATA TO SERVER *******************************************************/
  
  //create an array of json objects containing records, compress it in gzip http compression format and send it to server
  public int postData() throws IllegalArgumentException, ClientProtocolException, 
  HttpHostConnectException, IOException
  {      
    Log.d("StoreAndForwardService", "postData()");

    //get size of array of records to send and reference to the last record
    int size = mToSendRecords.size();
    if(size == 0)
      return -1;
    
    int sepIndex = 1; //default is '.' separator (see Constants.separators array)
    if((Utils.report_country != null)&&(Utils.report_country.equals("IT")))
      sepIndex = 0; //0 is for '-' separator (for italian CSP server)
    
    Record lastToSendRecord = mToSendRecords.get(size-1);
    Log.d("StoreAndForwardService", "postData()--> # of records: "+size);
    
    //save timestamp
    long lastTimestamp = 0;    
    if(lastToSendRecord.mSysTimestamp > 0)
      lastTimestamp = lastToSendRecord.mSysTimestamp;
    else
      lastTimestamp = lastToSendRecord.mBoxTimestamp;
    
    String lastTsFormatted = new SimpleDateFormat("yyyy-MM-dd_HH:mm:ss.SSSz", Locale.US).format(new Date(lastTimestamp));

    //********* MAKING OF HTTP HEADER **************
    
      DefaultHttpClient httpClient = new DefaultHttpClient();    
      HttpPost httpPost = new HttpPost(Utils.report_url);       
      
      httpPost.setHeader("Content-Encoding", "gzip");
      httpPost.setHeader("Content-Type", "application/json");
      httpPost.setHeader("Accept", "application/json");
      httpPost.setHeader("User-Agent", "AirProbe"+Utils.appVer);

      //******** authorization bearer header ********
      
      //air probe can be used also anymously. If account activation state is true (--> AirProbe activated) add this header
      if(Utils.getAccountActivationState(getApplicationContext()))
        httpPost.setHeader("Authorization", "Bearer "+Utils.getAccessToken(getApplicationContext()));
      
      //******** meta header (for new version API V1)
      
      httpPost.setHeader("meta"+Constants.separators[sepIndex]+"timestampRecorded", lastTsFormatted);   
      //httpPost.setHeader("meta"+Constants.separators[sepIndex]+"sessionId", lastToSendRecord.mSessionId); //deprecated from AP 1.4
      httpPost.setHeader("meta"+Constants.separators[sepIndex]+"deviceId", Utils.deviceID);
      httpPost.setHeader("meta"+Constants.separators[sepIndex]+"installId", Utils.installID);
      //httpPost.setHeader("meta"+Constants.separators[sepIndex]+"userFeedId", "");
      //httpPost.setHeader("meta"+Constants.separators[sepIndex]+"eventFeedId", "");
      httpPost.setHeader("meta"+Constants.separators[sepIndex]+"visibilityEvent", Constants.DATA_VISIBILITY[0]);
      httpPost.setHeader("meta"+Constants.separators[sepIndex]+"visibilityGlobal", Constants.DATA_VISIBILITY[0]); //set meta.visibilityGlobal=DETAILS for testing (to retrieve data after insertion)
      
      /* from AP 1.4 */
      if((lastToSendRecord.mBoxMac != null)&&(!lastToSendRecord.mBoxMac.equals("")))
      {        
        httpPost.setHeader("meta"+Constants.separators[sepIndex]+"sourceId", lastToSendRecord.mBoxMac);
      }
      
      httpPost.setHeader("meta"+Constants.separators[sepIndex]+"sourceSessionId"+Constants.separators[sepIndex]+"seed", lastToSendRecord.mSourceSessionSeed);
      httpPost.setHeader("meta"+Constants.separators[sepIndex]+"sourceSessionId"+Constants.separators[sepIndex]+"number", String.valueOf(lastToSendRecord.mSourceSessionNumber));
      httpPost.setHeader("meta"+Constants.separators[sepIndex]+"sourceSessionPointNumber", String.valueOf(lastToSendRecord.mSourcePointNumber));
      
      if((lastToSendRecord.mSemanticSessionSeed != null)&&(!lastToSendRecord.mSemanticSessionSeed.equals("")))
      {
        httpPost.setHeader("meta"+Constants.separators[sepIndex]+"semanticSessionId"+Constants.separators[sepIndex]+"seed", lastToSendRecord.mSemanticSessionSeed);
        httpPost.setHeader("meta"+Constants.separators[sepIndex]+"semanticSessionId"+Constants.separators[sepIndex]+"number", String.valueOf(lastToSendRecord.mSemanticSessionNumber));
        httpPost.setHeader("meta"+Constants.separators[sepIndex]+"semanticSessionPointNumber", String.valueOf(lastToSendRecord.mSemanticPointNumber));
      }
      
      httpPost.setHeader("data"+Constants.separators[sepIndex]+"contentDetails"+Constants.separators[sepIndex]+"typeVersion", "30"); //update by increment this field on header changes
      
      /* end of from AP 1.4 */
      
      //******** data header (for new version API V1)
      
      httpPost.setHeader("data"+Constants.separators[sepIndex]+"contentDetails"+Constants.separators[sepIndex]+"type", "airprobe_report");
      httpPost.setHeader("data"+Constants.separators[sepIndex]+"contentDetails"+Constants.separators[sepIndex]+"format", "json");
      //httpPost.setHeader("data"+Constants.separators[sepIndex]+"contentDetails"+Constants.separators[sepIndex]+"specification", "a-3"); //deprecated from ap v1.4  
      if(size > 1)
        httpPost.setHeader("data"+Constants.separators[sepIndex]+"contentDetails"+Constants.separators[sepIndex]+"list", "true");
      else
        httpPost.setHeader("data"+Constants.separators[sepIndex]+"contentDetails"+Constants.separators[sepIndex]+"list", "false");      
      httpPost.setHeader("data"+Constants.separators[sepIndex]+"contentDetails"+Constants.separators[sepIndex]+"listSize", String.valueOf(size));
      
      //******** geo header (for new version API V1)

      //add the right provider to header
      if(lastToSendRecord.mGpsProvider.equals(Constants.GPS_PROVIDERS[0])) //sensor box
      {
        httpPost.setHeader("geo"+Constants.separators[sepIndex]+"longitude", String.valueOf(lastToSendRecord.mBoxLon));
         httpPost.setHeader("geo"+Constants.separators[sepIndex]+"latitude", String.valueOf(lastToSendRecord.mBoxLat));
         if(lastToSendRecord.mBoxAcc != 0)
           httpPost.setHeader("geo"+Constants.separators[sepIndex]+"hdop", String.valueOf(lastToSendRecord.mBoxAcc)); //from AP 1.4
         if(lastToSendRecord.mBoxAltitude != 0)
           httpPost.setHeader("geo"+Constants.separators[sepIndex]+"altitude", String.valueOf(lastToSendRecord.mBoxAltitude)); //from AP 1.4
          if(lastToSendRecord.mBoxSpeed != 0)
            httpPost.setHeader("geo"+Constants.separators[sepIndex]+"speed", String.valueOf(lastToSendRecord.mBoxSpeed)); //from AP 1.4
         if(lastToSendRecord.mBoxBear != 0)
           httpPost.setHeader("geo"+Constants.separators[sepIndex]+"bearing", String.valueOf(lastToSendRecord.mBoxBear)); //from AP 1.4         
         httpPost.setHeader("geo"+Constants.separators[sepIndex]+"provider", lastToSendRecord.mGpsProvider);
         httpPost.setHeader("geo"+Constants.separators[sepIndex]+"timestamp", lastTsFormatted);
      }
      else if(lastToSendRecord.mGpsProvider.equals(Constants.GPS_PROVIDERS[1])) //phone
      {
        httpPost.setHeader("geo"+Constants.separators[sepIndex]+"longitude", String.valueOf(lastToSendRecord.mPhoneLon));
         httpPost.setHeader("geo"+Constants.separators[sepIndex]+"latitude", String.valueOf(lastToSendRecord.mPhoneLat));
         if(lastToSendRecord.mPhoneAcc != 0)
           httpPost.setHeader("geo"+Constants.separators[sepIndex]+"accuracy", String.valueOf(lastToSendRecord.mPhoneAcc));
         if(lastToSendRecord.mPhoneAltitude != 0)
           httpPost.setHeader("geo"+Constants.separators[sepIndex]+"altitude", String.valueOf(lastToSendRecord.mPhoneAltitude)); //from AP 1.4
         if(lastToSendRecord.mPhoneSpeed != 0)
           httpPost.setHeader("geo"+Constants.separators[sepIndex]+"speed", String.valueOf(lastToSendRecord.mPhoneSpeed)); //from AP 1.4
         if(lastToSendRecord.mPhoneBear != 0)
           httpPost.setHeader("geo"+Constants.separators[sepIndex]+"bearing", String.valueOf(lastToSendRecord.mPhoneBear)); //from AP 1.4
         httpPost.setHeader("geo"+Constants.separators[sepIndex]+"provider", lastToSendRecord.mGpsProvider);
         httpPost.setHeader("geo"+Constants.separators[sepIndex]+"timestamp", lastTsFormatted);
      }
      else if(lastToSendRecord.mGpsProvider.equals(Constants.GPS_PROVIDERS[2])) //network
      {
        httpPost.setHeader("geo"+Constants.separators[sepIndex]+"longitude", String.valueOf(lastToSendRecord.mNetworkLon));
         httpPost.setHeader("geo"+Constants.separators[sepIndex]+"latitude", String.valueOf(lastToSendRecord.mNetworkLat));
         if(lastToSendRecord.mNetworkAcc != 0)
           httpPost.setHeader("geo"+Constants.separators[sepIndex]+"accuracy", String.valueOf(lastToSendRecord.mNetworkAcc));
         if(lastToSendRecord.mNetworkAltitude != 0)         
           httpPost.setHeader("geo"+Constants.separators[sepIndex]+"altitude", String.valueOf(lastToSendRecord.mNetworkAltitude)); //from AP 1.4
         if(lastToSendRecord.mNetworkSpeed != 0)         
           httpPost.setHeader("geo"+Constants.separators[sepIndex]+"speed", String.valueOf(lastToSendRecord.mNetworkSpeed)); //from AP 1.4
         if(lastToSendRecord.mNetworkBear != 0)         
           httpPost.setHeader("geo"+Constants.separators[sepIndex]+"bearing", String.valueOf(lastToSendRecord.mNetworkBear)); //from AP 1.4          
         httpPost.setHeader("geo"+Constants.separators[sepIndex]+"provider", lastToSendRecord.mGpsProvider);
         httpPost.setHeader("geo"+Constants.separators[sepIndex]+"timestamp", lastTsFormatted);
      }   
      
      //******** MAKING OF HTTP CONTENT (JSON) *************
      
      //writing string content as an array of json object
    StringBuilder sb = new StringBuilder();
    sb.append("[");

    for(int i = 0; i < mToSendRecords.size(); i++)
    {
      sb.append(mToSendRecords.get(i).toJson().toString());        
      if((size != 0)&&(i != size-1))
        sb.append(",");            
      sb.append("\n");
    }      
    sb.append("]");
    
    Log.d("StoreAndForwardService", "postData()--> json: " +sb.toString());
    Log.d("StoreAndForwardService", "postData()--> access token: "+Utils.getAccessToken(getApplicationContext()));

    //compress json content into byte array entity
    byte[] contentGzippedBytes = zipStringToBytes(sb.toString());
    ByteArrayEntity byteArrayEntity = new ByteArrayEntity(contentGzippedBytes);    
    byteArrayEntity.setChunked(false); //IMPORTANT: put false for smartcity.csp.it server

      httpPost.setEntity(byteArrayEntity);
      
      Log.d("StoreAndForwardService", "postData()--> Content length: " +httpPost.getEntity().getContentLength());
    Log.d("StoreAndForwardService", "postData()--> Method: " +httpPost.getMethod());
  
    //do http post (it performs asynchronously)
    HttpResponse response = httpClient.execute(httpPost);
  
    sb = null;

    //server response
    Log.d("StoreAndForwardService", "postData()--> status line: " +response.getStatusLine()); 
    
    httpClient.getConnectionManager().shutdown();
            
    //server response, status line
    StatusLine statusLine = response.getStatusLine();
    Log.d("StoreAndForwardService", "postData()--> status code: " +statusLine.getStatusCode());
    return statusLine.getStatusCode(); 
  }  
  
  //create an array of json objects containing records, compress it in gzip http compression format and send it to server
  //makes a secure (https://) http post
  public int postSecureData() throws IllegalArgumentException, ClientProtocolException, 
  HttpHostConnectException, IOException
  {      
    Log.d("StoreAndForwardService", "postSecureData()");

    //get size of array of records to send and reference to the last record
    int size = mToSendRecords.size();
    if(size == 0)
      return -1;
    
    int sepIndex = 1; //default is '.' separator (see Constants.separators array)
    if((Utils.report_country != null)&&(Utils.report_country.equals("IT")))
      sepIndex = 0; //0 is for '-' separator (for italian CSP server)
    
    Record lastToSendRecord = mToSendRecords.get(size-1);
    Log.d("StoreAndForwardService", "postSecureData()--> # of records: "+size);
    
    //save timestamp
    long lastTimestamp = 0;    
    if(lastToSendRecord.mSysTimestamp > 0)
      lastTimestamp = lastToSendRecord.mSysTimestamp;
    else
      lastTimestamp = lastToSendRecord.mBoxTimestamp;
    
    String lastTsFormatted = new SimpleDateFormat("yyyy-MM-dd_HH:mm:ss.SSSz", Locale.US).format(new Date(lastTimestamp));

    //********* MAKING OF HTTP HEADER **************

    URL url = new URL(Utils.report_url);
    HttpsURLConnection con = (HttpsURLConnection)url.openConnection();
      
      con.setRequestMethod("POST");
      con.setUseCaches(false);
      con.setDoInput(true);
    con.setDoOutput(true);
    
    con.setRequestProperty("Content-Encoding", "gzip");
    con.setRequestProperty("Content-Type", "application/json");
    con.setRequestProperty("Accept", "application/json");
    con.setRequestProperty("User-Agent", "AirProbe"+Utils.appVer);

      //******** authorization bearer header ********
      
    //air probe can be used also anymously. If account activation state is true (--> AirProbe activated) add this header
    if(Utils.getAccountActivationState(getApplicationContext()))
      con.setRequestProperty("Authorization", "Bearer "+Utils.getAccessToken(getApplicationContext()));
    else if(Utils.getAccountActivationStateForClient(getApplicationContext()))
      con.setRequestProperty("Authorization", "Bearer "+Utils.getAccessTokenForClient(getApplicationContext()));
    
      //******** meta header (for new version API V1)
      
    con.setRequestProperty("meta"+Constants.separators[sepIndex]+"timestampRecorded", lastTsFormatted);
    //con.setRequestProperty("meta"+Constants.separators[sepIndex]+"sessionId", lastToSendRecord.mSessionId); //deprecated from AP 1.4
    con.setRequestProperty("meta"+Constants.separators[sepIndex]+"deviceId", Utils.deviceID);
    con.setRequestProperty("meta"+Constants.separators[sepIndex]+"installId", Utils.installID);
    //con.setRequestProperty("meta"+Constants.separators[sepIndex]+"userFeedId", "");
    //con.setRequestProperty("meta"+Constants.separators[sepIndex]+"eventFeedId", "");
    con.setRequestProperty("meta"+Constants.separators[sepIndex]+"visibilityEvent", Constants.DATA_VISIBILITY[0]);
    con.setRequestProperty("meta"+Constants.separators[sepIndex]+"visibilityGlobal", Constants.DATA_VISIBILITY[0]);  //set meta.visibilityGlobal=DETAILS for testing (to retrieve data after insertion)  
      
    /* from AP 1.4 */
      if((lastToSendRecord.mBoxMac != null)&&(!lastToSendRecord.mBoxMac.equals("")))
      {
        Log.d("StoreAndForwardService", "postSecureData()--> box mac address: "+lastToSendRecord.mBoxMac);
        con.setRequestProperty("meta"+Constants.separators[sepIndex]+"sourceId", lastToSendRecord.mBoxMac);
      }
      else
        con.setRequestProperty("meta"+Constants.separators[sepIndex]+"sourceId", Utils.getDeviceAddress(getApplicationContext()));
      
      con.setRequestProperty("meta"+Constants.separators[sepIndex]+"sourceSessionId"+Constants.separators[sepIndex]+"seed", lastToSendRecord.mSourceSessionSeed);
      con.setRequestProperty("meta"+Constants.separators[sepIndex]+"sourceSessionId"+Constants.separators[sepIndex]+"number", String.valueOf(lastToSendRecord.mSourceSessionNumber));
      con.setRequestProperty("meta"+Constants.separators[sepIndex]+"sourceSessionPointNumber", String.valueOf(lastToSendRecord.mSourcePointNumber));
      
      if((lastToSendRecord.mSemanticSessionSeed != null)&&(!lastToSendRecord.mSemanticSessionSeed.equals("")))
      {
        con.setRequestProperty("meta"+Constants.separators[sepIndex]+"semanticSessionId"+Constants.separators[sepIndex]+"seed", lastToSendRecord.mSemanticSessionSeed);
        con.setRequestProperty("meta"+Constants.separators[sepIndex]+"semanticSessionId"+Constants.separators[sepIndex]+"number", String.valueOf(lastToSendRecord.mSemanticSessionNumber));
        con.setRequestProperty("meta"+Constants.separators[sepIndex]+"semanticSessionPointNumber", String.valueOf(lastToSendRecord.mSemanticPointNumber));
      }
      
      con.setRequestProperty("data"+Constants.separators[sepIndex]+"contentDetails"+Constants.separators[sepIndex]+"typeVersion", "30"); //update by increment this field on header changes      
      /* end of from AP 1.4 */
    
      //******** data header (for new version API V1)
      
    con.setRequestProperty("data"+Constants.separators[sepIndex]+"contentDetails"+Constants.separators[sepIndex]+"type", "airprobe_report");
    con.setRequestProperty("data"+Constants.separators[sepIndex]+"contentDetails"+Constants.separators[sepIndex]+"format", "json");
    //con.setRequestProperty("data"+Constants.separators[sepIndex]+"contentDetails"+Constants.separators[sepIndex]+"specification", "a-3"); //deprecated from AP 1.4
      if(size > 1)
        con.setRequestProperty("data"+Constants.separators[sepIndex]+"contentDetails"+Constants.separators[sepIndex]+"list", "true");
      else
        con.setRequestProperty("data"+Constants.separators[sepIndex]+"contentDetails"+Constants.separators[sepIndex]+"list", "false");
      con.setRequestProperty("data"+Constants.separators[sepIndex]+"contentDetails"+Constants.separators[sepIndex]+"listSize", String.valueOf(size));

      //******** geo header (for new version API V1)

      //add the right provider to header
      if(lastToSendRecord.mGpsProvider.equals(Constants.GPS_PROVIDERS[0])) //sensor box
      {
        con.setRequestProperty("geo"+Constants.separators[sepIndex]+"longitude", String.valueOf(lastToSendRecord.mBoxLon));
        con.setRequestProperty("geo"+Constants.separators[sepIndex]+"latitude", String.valueOf(lastToSendRecord.mBoxLat));
         if(lastToSendRecord.mBoxAcc != 0)
           con.setRequestProperty("geo"+Constants.separators[sepIndex]+"hdop", String.valueOf(lastToSendRecord.mBoxAcc)); //from AP 1.4
         if(lastToSendRecord.mBoxAltitude != 0)
           con.setRequestProperty("geo"+Constants.separators[sepIndex]+"altitude", String.valueOf(lastToSendRecord.mBoxAltitude)); //from AP 1.4
          if(lastToSendRecord.mBoxSpeed != 0)
          con.setRequestProperty("geo"+Constants.separators[sepIndex]+"speed", String.valueOf(lastToSendRecord.mBoxSpeed)); //from AP 1.4
         if(lastToSendRecord.mBoxBear != 0)
           con.setRequestProperty("geo"+Constants.separators[sepIndex]+"bearing", String.valueOf(lastToSendRecord.mBoxBear)); //from AP 1.4         
         con.setRequestProperty("geo"+Constants.separators[sepIndex]+"provider", lastToSendRecord.mGpsProvider);
         con.setRequestProperty("geo"+Constants.separators[sepIndex]+"timestamp", lastTsFormatted);
      }
      else if(lastToSendRecord.mGpsProvider.equals(Constants.GPS_PROVIDERS[1])) //phone
      {
        con.setRequestProperty("geo"+Constants.separators[sepIndex]+"longitude", String.valueOf(lastToSendRecord.mPhoneLon));
        con.setRequestProperty("geo"+Constants.separators[sepIndex]+"latitude", String.valueOf(lastToSendRecord.mPhoneLat));
         if(lastToSendRecord.mPhoneAcc != 0)
           con.setRequestProperty("geo"+Constants.separators[sepIndex]+"accuracy", String.valueOf(lastToSendRecord.mPhoneAcc));
         if(lastToSendRecord.mPhoneAltitude != 0)
           con.setRequestProperty("geo"+Constants.separators[sepIndex]+"altitude", String.valueOf(lastToSendRecord.mPhoneAltitude)); //from AP 1.4
         if(lastToSendRecord.mPhoneSpeed != 0)
           con.setRequestProperty("geo"+Constants.separators[sepIndex]+"speed", String.valueOf(lastToSendRecord.mPhoneSpeed)); //from AP 1.4
         if(lastToSendRecord.mPhoneBear != 0)
           con.setRequestProperty("geo"+Constants.separators[sepIndex]+"bearing", String.valueOf(lastToSendRecord.mPhoneBear)); //from AP 1.4
         con.setRequestProperty("geo"+Constants.separators[sepIndex]+"provider", lastToSendRecord.mGpsProvider);
         con.setRequestProperty("geo"+Constants.separators[sepIndex]+"timestamp", lastTsFormatted);
      }
      else if(lastToSendRecord.mGpsProvider.equals(Constants.GPS_PROVIDERS[2])) //network
      {
        con.setRequestProperty("geo"+Constants.separators[sepIndex]+"longitude", String.valueOf(lastToSendRecord.mNetworkLon));
        con.setRequestProperty("geo"+Constants.separators[sepIndex]+"latitude", String.valueOf(lastToSendRecord.mNetworkLat));
         if(lastToSendRecord.mNetworkAcc != 0)
           con.setRequestProperty("geo"+Constants.separators[sepIndex]+"accuracy", String.valueOf(lastToSendRecord.mNetworkAcc));
         if(lastToSendRecord.mNetworkAltitude != 0)         
           con.setRequestProperty("geo"+Constants.separators[sepIndex]+"altitude", String.valueOf(lastToSendRecord.mNetworkAltitude)); //from AP 1.4
         if(lastToSendRecord.mNetworkSpeed != 0)         
           con.setRequestProperty("geo"+Constants.separators[sepIndex]+"speed", String.valueOf(lastToSendRecord.mNetworkSpeed)); //from AP 1.4
         if(lastToSendRecord.mNetworkBear != 0)         
           con.setRequestProperty("geo"+Constants.separators[sepIndex]+"bearing", String.valueOf(lastToSendRecord.mNetworkBear)); //from AP 1.4          
         con.setRequestProperty("geo"+Constants.separators[sepIndex]+"provider", lastToSendRecord.mGpsProvider);
         con.setRequestProperty("geo"+Constants.separators[sepIndex]+"timestamp", lastTsFormatted);
      }  
      
      //******** MAKING OF HTTP CONTENT (JSON) *************
      
      //writing string content as an array of json object
    StringBuilder sb = new StringBuilder();
    sb.append("[");

    for(int i = 0; i < mToSendRecords.size(); i++)
    {
      sb.append(mToSendRecords.get(i).toJson().toString());        
      if((size != 0)&&(i != size-1))
        sb.append(",");            
      sb.append("\n");
    }      
    sb.append("]");
    
    //Log.d("StoreAndForwardService", "postSecureData()--> json: " +sb.toString());
    //Log.d("StoreAndForwardService", "postSecureData()--> access token: "+Utils.getAccessToken(getApplicationContext()));

    //compress json content into byte array entity
    byte[] contentGzippedBytes = zipStringToBytes(sb.toString());
    
      //write json compressed content into output stream
    OutputStream outputStream = con.getOutputStream();
    outputStream.write(contentGzippedBytes);
    outputStream.flush();
    outputStream.close();
    
    int responseCode = con.getResponseCode();
    
    Log.d("StoreAndForwardService", "postSecureData()--> response code: " +responseCode);

    sb = null;

    return responseCode; 
  }    
  
  //create an array of json objects containing only tags, compress it in gzip http compression format and send it to server
  public void postTags() throws IllegalArgumentException, ClientProtocolException, HttpHostConnectException, IOException
  {      
    Log.d("StoreAndForwardService", "postTags()");
    
    int sepIndex = 1; //default is '.' separator (see Constants.separators array)
    if((Utils.report_country != null)&&(Utils.report_country.equals("IT")))
      sepIndex = 0; //0 is for '-' separator (for italian CSP server)
    
    List<String> sids = mDbManager.getSidsOfRecordsWithTags();

    if((sids != null)&&(sids.size() > 0))
    {
      for(int i = 0; i < sids.size(); i++)
      {
        String sessionId = (String)sids.get(i);
            
        //load records containing user tags with actual session id
        List<Record>recordsWithTags = mDbManager.loadRecordsWithTagBySessionId(sessionId);
        
        if((recordsWithTags != null)&&(recordsWithTags.size() > 0))
        {
          //get size of array of records containing tags and reference to the last record
          int size = recordsWithTags.size();
          
          //obtain reference to the last record of serie
          Record lastToSendRecord = recordsWithTags.get(size-1);
          Log.d("StoreAndForwardService", "postTags()--> # of records containing tags: "+size);
          
          //save timestamp of last record containing tags
          long lastTimestamp = 0;    
          if(lastToSendRecord.mSysTimestamp > 0)
            lastTimestamp = lastToSendRecord.mSysTimestamp;
          else
            lastTimestamp = lastToSendRecord.mBoxTimestamp;
          
          String lastTsFormatted = new SimpleDateFormat("yyyy-MM-dd_HH:mm:ss.SSSz", Locale.US).format(new Date(lastTimestamp));
        
          //********* MAKING OF HTTP HEADER **************
          
            DefaultHttpClient httpClient = new DefaultHttpClient();    
            HttpPost httpPost = new HttpPost(Utils.report_url);       
            
            httpPost.setHeader("Content-Encoding", "gzip");
            httpPost.setHeader("Content-Type", "application/json");
            httpPost.setHeader("Accept", "application/json");
            httpPost.setHeader("User-Agent", "AirProbe"+Utils.appVer);

            // ******* authorization bearer header ********
           
            httpPost.setHeader("Authorization", "Bearer "+Utils.getAccessToken(getApplicationContext()));
            
            //******** meta header (for new version API V1)
            
            httpPost.setHeader("meta"+Constants.separators[sepIndex]+"timestampRecorded", lastTsFormatted);   
            httpPost.setHeader("meta"+Constants.separators[sepIndex]+"deviceId", Utils.deviceID);
            httpPost.setHeader("meta"+Constants.separators[sepIndex]+"installId", Utils.installID);
            
            //******** data header (for new version API V1)
            
            //httpPost.setHeader("data"+Constants.separators[sepIndex]+"extendedPacketId", "");
            //httpPost.setHeader("data"+Constants.separators[sepIndex]+"extendedPacketPointId", "");
            //httpPost.setHeader("data"+Constants.separators[sepIndex]+"extendedSessionId", ""); //deprecated from AP 1.4
            
            httpPost.setHeader("data"+Constants.separators[sepIndex]+"contentDetails"+Constants.separators[sepIndex]+"type", "airprobe_tags");
            httpPost.setHeader("data"+Constants.separators[sepIndex]+"contentDetails"+Constants.separators[sepIndex]+"format", "json");
            //httpPost.setHeader("data"+Constants.separators[sepIndex]+"contentDetails"+Constants.separators[sepIndex]+"specification", "at-3");  //deprecated from AP 1.4
            if(size > 1)
              httpPost.setHeader("data"+Constants.separators[sepIndex]+"contentDetails"+Constants.separators[sepIndex]+"list", "true");
            else
              httpPost.setHeader("data"+Constants.separators[sepIndex]+"contentDetails"+Constants.separators[sepIndex]+"list", "false");      
            httpPost.setHeader("data"+Constants.separators[sepIndex]+"contentDetails"+Constants.separators[sepIndex]+"listSize", String.valueOf(size));
            
            /* from AP 1.4 */
            if((lastToSendRecord.mBoxMac != null)&&(!lastToSendRecord.mBoxMac.equals("")))
              httpPost.setHeader("meta"+Constants.separators[sepIndex]+"sourceId", lastToSendRecord.mBoxMac);
            
            httpPost.setHeader("data"+Constants.separators[sepIndex]+"extendedSourceSessionId"+Constants.separators[sepIndex]+"seed", lastToSendRecord.mSourceSessionSeed);
            httpPost.setHeader("data"+Constants.separators[sepIndex]+"extendedSourceSessionId"+Constants.separators[sepIndex]+"number", String.valueOf(lastToSendRecord.mSourceSessionNumber));
            if((lastToSendRecord.mSemanticSessionSeed != null)&&(!lastToSendRecord.mSemanticSessionSeed.equals("")))
            {
              httpPost.setHeader("data"+Constants.separators[sepIndex]+"extendedSemanticSessionId"+Constants.separators[sepIndex]+"seed", lastToSendRecord.mSemanticSessionSeed);
              httpPost.setHeader("data"+Constants.separators[sepIndex]+"extendedSemanticSessionId"+Constants.separators[sepIndex]+"number", String.valueOf(lastToSendRecord.mSemanticSessionNumber));
            }
            
            httpPost.setHeader("data"+Constants.separators[sepIndex]+"contentDetails"+Constants.separators[sepIndex]+"typeVersion", "30"); //update by increment this field on header changes            
            /* end of from AP 1.4 */
            
            //******** geo header (for new version API V1)
            
            //add the right provider to header
            if(lastToSendRecord.mGpsProvider.equals(Constants.GPS_PROVIDERS[0])) //sensor box
            {
              httpPost.setHeader("geo"+Constants.separators[sepIndex]+"longitude", String.valueOf(lastToSendRecord.mBoxLon));
               httpPost.setHeader("geo"+Constants.separators[sepIndex]+"latitude", String.valueOf(lastToSendRecord.mBoxLat));
               if(lastToSendRecord.mBoxAcc != 0)
                 httpPost.setHeader("geo"+Constants.separators[sepIndex]+"hdop", String.valueOf(lastToSendRecord.mBoxAcc)); //from AP 1.4
               if(lastToSendRecord.mBoxAltitude != 0)
                 httpPost.setHeader("geo"+Constants.separators[sepIndex]+"altitude", String.valueOf(lastToSendRecord.mBoxAltitude)); //from AP 1.4
                if(lastToSendRecord.mBoxSpeed != 0)
                  httpPost.setHeader("geo"+Constants.separators[sepIndex]+"speed", String.valueOf(lastToSendRecord.mBoxSpeed)); //from AP 1.4
               if(lastToSendRecord.mBoxBear != 0)
                 httpPost.setHeader("geo"+Constants.separators[sepIndex]+"bearing", String.valueOf(lastToSendRecord.mBoxBear)); //from AP 1.4         
               httpPost.setHeader("geo"+Constants.separators[sepIndex]+"provider", lastToSendRecord.mGpsProvider);
               httpPost.setHeader("geo"+Constants.separators[sepIndex]+"timestamp", lastTsFormatted);
            }
            else if(lastToSendRecord.mGpsProvider.equals(Constants.GPS_PROVIDERS[1])) //phone
            {
              httpPost.setHeader("geo"+Constants.separators[sepIndex]+"longitude", String.valueOf(lastToSendRecord.mPhoneLon));
               httpPost.setHeader("geo"+Constants.separators[sepIndex]+"latitude", String.valueOf(lastToSendRecord.mPhoneLat));
               if(lastToSendRecord.mPhoneAcc != 0)
                 httpPost.setHeader("geo"+Constants.separators[sepIndex]+"accuracy", String.valueOf(lastToSendRecord.mPhoneAcc));
               if(lastToSendRecord.mPhoneAltitude != 0)
                 httpPost.setHeader("geo"+Constants.separators[sepIndex]+"altitude", String.valueOf(lastToSendRecord.mPhoneAltitude)); //from AP 1.4
               if(lastToSendRecord.mPhoneSpeed != 0)
                 httpPost.setHeader("geo"+Constants.separators[sepIndex]+"speed", String.valueOf(lastToSendRecord.mPhoneSpeed)); //from AP 1.4
               if(lastToSendRecord.mPhoneBear != 0)
                 httpPost.setHeader("geo"+Constants.separators[sepIndex]+"bearing", String.valueOf(lastToSendRecord.mPhoneBear)); //from AP 1.4
               httpPost.setHeader("geo"+Constants.separators[sepIndex]+"provider", lastToSendRecord.mGpsProvider);
               httpPost.setHeader("geo"+Constants.separators[sepIndex]+"timestamp", lastTsFormatted);
            }
            else if(lastToSendRecord.mGpsProvider.equals(Constants.GPS_PROVIDERS[2])) //network
            {
              httpPost.setHeader("geo"+Constants.separators[sepIndex]+"longitude", String.valueOf(lastToSendRecord.mNetworkLon));
               httpPost.setHeader("geo"+Constants.separators[sepIndex]+"latitude", String.valueOf(lastToSendRecord.mNetworkLat));
               if(lastToSendRecord.mNetworkAcc != 0)
                 httpPost.setHeader("geo"+Constants.separators[sepIndex]+"accuracy", String.valueOf(lastToSendRecord.mNetworkAcc));
               if(lastToSendRecord.mNetworkAltitude != 0)         
                 httpPost.setHeader("geo"+Constants.separators[sepIndex]+"altitude", String.valueOf(lastToSendRecord.mNetworkAltitude)); //from AP 1.4
               if(lastToSendRecord.mNetworkSpeed != 0)         
                 httpPost.setHeader("geo"+Constants.separators[sepIndex]+"speed", String.valueOf(lastToSendRecord.mNetworkSpeed)); //from AP 1.4
               if(lastToSendRecord.mNetworkBear != 0)         
                 httpPost.setHeader("geo"+Constants.separators[sepIndex]+"bearing", String.valueOf(lastToSendRecord.mNetworkBear)); //from AP 1.4          
               httpPost.setHeader("geo"+Constants.separators[sepIndex]+"provider", lastToSendRecord.mGpsProvider);
               httpPost.setHeader("geo"+Constants.separators[sepIndex]+"timestamp", lastTsFormatted);
            }   
            
            //******** MAKING OF HTTP CONTENT (JSON) *************
            
            //writing string content as an array of json object
            StringBuilder sb = new StringBuilder();
          sb.append("[");
          
          JSONObject object = new JSONObject();
          
          try 
          {
            object.put("timestamp", lastTimestamp);
            
            JSONArray locations = new JSONArray();
            
            //sensor box gps data
            if(lastToSendRecord.mBoxLat != 0)
            {
              JSONObject boxLocation = new JSONObject();
              boxLocation.put("latitude", lastToSendRecord.mBoxLat);
              boxLocation.put("longitude", lastToSendRecord.mBoxLon);
              if(lastToSendRecord.mBoxAcc != 0)
                boxLocation.put("hdpop", lastToSendRecord.mBoxAcc);
              if(lastToSendRecord.mBoxAltitude != 0)
                boxLocation.put("altitude", lastToSendRecord.mBoxAltitude);
              if(lastToSendRecord.mBoxSpeed != 0)
                boxLocation.put("speed", lastToSendRecord.mBoxSpeed);
              if(lastToSendRecord.mBoxBear != 0)
                boxLocation.put("bearing", lastToSendRecord.mBoxBear);
              boxLocation.put("provider", Constants.GPS_PROVIDERS[0]);
              boxLocation.put("timestamp", lastToSendRecord.mBoxTimestamp);
              
              locations.put(0, boxLocation);
            }
            
            //phone gps data
            if(lastToSendRecord.mPhoneLat != 0)
            {
              JSONObject phoneLocation = new JSONObject();
              phoneLocation.put("latitude", lastToSendRecord.mPhoneLat);
              phoneLocation.put("longitude", lastToSendRecord.mPhoneLon);
              if(lastToSendRecord.mPhoneAcc != 0)
                phoneLocation.put("accuracy", lastToSendRecord.mPhoneAcc);
              if(lastToSendRecord.mPhoneAltitude != 0)
                phoneLocation.put("altitude", lastToSendRecord.mPhoneAltitude);
              if(lastToSendRecord.mPhoneSpeed != 0)
                phoneLocation.put("speed", lastToSendRecord.mPhoneSpeed);
              if(lastToSendRecord.mPhoneBear != 0)
                phoneLocation.put("bearing", lastToSendRecord.mPhoneBear);
              phoneLocation.put("provider", Constants.GPS_PROVIDERS[1]);
              phoneLocation.put("timestamp", lastToSendRecord.mPhoneTimestamp);
              
              locations.put(1, phoneLocation);
            }
            
            //network gps data
            if(lastToSendRecord.mNetworkLat != 0)
            {
              JSONObject netLocation = new JSONObject();
              netLocation.put("latitude", lastToSendRecord.mNetworkLat);
              netLocation.put("longitude", lastToSendRecord.mNetworkLon);
              if(lastToSendRecord.mNetworkAcc != 0)
                netLocation.put("accuracy", lastToSendRecord.mNetworkAcc);
              if(lastToSendRecord.mNetworkAltitude != 0)
                netLocation.put("altitude", lastToSendRecord.mNetworkAltitude);
              if(lastToSendRecord.mNetworkSpeed != 0)
                netLocation.put("speed", lastToSendRecord.mNetworkSpeed);
              if(lastToSendRecord.mNetworkBear != 0)
                netLocation.put("bearing", lastToSendRecord.mNetworkBear);        
              netLocation.put("provider", Constants.GPS_PROVIDERS[2]);
              netLocation.put("timestamp", lastToSendRecord.mNetworkTimestamp);

              locations.put(2, netLocation);
            }
            
            object.put("locations", locations);
          }
          catch (JSONException e) 
          {
            e.printStackTrace();
          }
          
          String concatOfTags = "";
          
          //put the tags of all records in concatOfTags string
          for(int j = 0; j < recordsWithTags.size(); j++)
          {
            if((recordsWithTags.get(j).mUserData1 != null)&&(!recordsWithTags.get(j).mUserData1.equals("")))
              concatOfTags += recordsWithTags.get(j).mUserData1+" ";
          }
          Log.d("StoreAndForwardService", "postTags()--> concat of tags: " +concatOfTags);
          
          try 
          {
            String[] tags = concatOfTags.split(" ");
            JSONArray tagsArray = new JSONArray();
            if((tags != null)&&(tags.length > 0))
            {
              for(int k = 0; k < tags.length; k++)
              {
                if(!tags[k].equals(""))
                  tagsArray.put(k, tags[k]);
              }
            }
            object.put("tags", tagsArray);
            //object.put("tags_cause", null);
            //object.put("tags_location", null);
            //object.put("tags_perception", null);
          } 
          catch (JSONException e) 
          {
            e.printStackTrace();
          }
          sb.append(object.toString());  
          sb.append("]");        
                
          //Log.d("StoreAndForwardService", "]");  
          Log.d("StoreAndForwardService", "postTags()--> json to string: " +sb.toString());
        
          byte[] contentGzippedBytes = zipStringToBytes(sb.toString());
          ByteArrayEntity byteArrayEntity = new ByteArrayEntity(contentGzippedBytes);
          
          byteArrayEntity.setChunked(false); //IMPORTANT: must put false for smartcity.csp.it server

          //IMPORTANT: do not set the content-Length, because is embedded in Entity
          //httpPost.setHeader("Content-Length", byteArrayEntity.getContentLength()+"");
            
          httpPost.setEntity(byteArrayEntity);
            
            Log.d("StoreAndForwardService", "postTags()--> Content length: " +httpPost.getEntity().getContentLength());
          Log.d("StoreAndForwardService", "postTags()--> Method: " +httpPost.getMethod());
        
          //do http post (it performs asynchronously)
          HttpResponse response = httpClient.execute(httpPost);
          
          sb = null;
          
          //server response
          //String responseBody = EntityUtils.toString(response.getEntity());
          //Log.d("StoreAndForwardService", "postTags()--> response: " +responseBody);
          Log.d("StoreAndForwardService", "postTags()--> status line: " +response.getStatusLine());   
          
          httpClient.getConnectionManager().shutdown();
                  
          //server response, status line
          StatusLine statusLine = response.getStatusLine();
          int statusCode = statusLine.getStatusCode();   
          
          if(statusCode == Constants.STATUS_OK)
          {
            Log.d("StoreAndForwardService", "postTags()--> STATUS OK");
            mDbManager.deleteRecordsWithTagsBySessionId(sessionId);          
          }
          else
            Log.d("StoreAndForwardService", "postTags()--> status error code: "+statusCode);
        }
        else
          Log.d("StoreAndForwardService", "postTags()--> no tags to send");
      }
    }
  }    
  
  //create an array of json objects containing only tags, compress it in gzip http compression format and send it to server
  public void postSecureTags() throws IllegalArgumentException, ClientProtocolException, HttpHostConnectException, IOException
  {      
    Log.d("StoreAndForwardService", "postSecureTags()");
    
    int sepIndex = 1; //default is '.' separator (see Constants.separators array)
    if((Utils.report_country != null)&&(Utils.report_country.equals("IT")))
      sepIndex = 0; //0 is for '-' separator (for italian CSP server)
    
    List<String> sids = mDbManager.getSidsOfRecordsWithTags();

    if((sids != null)&&(sids.size() > 0))
    {
      for(int i = 0; i < sids.size(); i++)
      {
        String sessionId = (String)sids.get(i);
            
        //load records containing user tags with actual session id
        List<Record>recordsWithTags = mDbManager.loadRecordsWithTagBySessionId(sessionId);
        
        if((recordsWithTags != null)&&(recordsWithTags.size() > 0))
        {
          //get size of array of records containing tags and reference to the last record
          int size = recordsWithTags.size();
          
          //obtain reference to the last record of serie
          Record lastToSendRecord = recordsWithTags.get(size-1);
          Log.d("StoreAndForwardService", "postSecureTags()--> # of records containing tags: "+size);
          
          //save timestamp of last record containing tags
          long lastTimestamp = 0;    
          if(lastToSendRecord.mSysTimestamp > 0)
            lastTimestamp = lastToSendRecord.mSysTimestamp;
          else
            lastTimestamp = lastToSendRecord.mBoxTimestamp;
          
          String lastTsFormatted = new SimpleDateFormat("yyyy-MM-dd_HH:mm:ss.SSSz", Locale.US).format(new Date(lastTimestamp));

          //********* MAKING OF HTTP HEADER **************

          URL url = new URL(Utils.report_url);
          HttpsURLConnection con = (HttpsURLConnection)url.openConnection();
            
            con.setRequestMethod("POST");
            con.setUseCaches(false);
            con.setDoInput(true);
          con.setDoOutput(true);
          
          con.setRequestProperty("Content-Encoding", "gzip");
          con.setRequestProperty("Content-Type", "application/json");
          con.setRequestProperty("Accept", "application/json");
          con.setRequestProperty("User-Agent", "AirProbe"+Utils.appVer);

            //******** authorization bearer header ********
            
          if(Utils.getAccountActivationState(getApplicationContext()))
            con.setRequestProperty("Authorization", "Bearer "+Utils.getAccessToken(getApplicationContext()));
          else if(Utils.getAccountActivationStateForClient(getApplicationContext()))
            con.setRequestProperty("Authorization", "Bearer "+Utils.getAccessTokenForClient(getApplicationContext()));
          
            //******** meta header (for new version API V1)
            
          con.setRequestProperty("meta"+Constants.separators[sepIndex]+"timestampRecorded", lastTsFormatted);
          con.setRequestProperty("meta"+Constants.separators[sepIndex]+"deviceId", Utils.deviceID);
          con.setRequestProperty("meta"+Constants.separators[sepIndex]+"installId", Utils.installID);
          
            //******** data header (for new version API V1)
            
          //con.setRequestProperty("data"+Constants.separators[sepIndex]+"extendedPacketId", "");
          //con.setRequestProperty("data"+Constants.separators[sepIndex]+"extendedPacketPointId", "");
          //con.setRequestProperty("data"+Constants.separators[sepIndex]+"extendedSessionId", ""); //deprecated from AP 1.4
            
          con.setRequestProperty("data"+Constants.separators[sepIndex]+"contentDetails"+Constants.separators[sepIndex]+"type", "airprobe_tags");
          con.setRequestProperty("data"+Constants.separators[sepIndex]+"contentDetails"+Constants.separators[sepIndex]+"format", "json");
          //con.setRequestProperty("data"+Constants.separators[sepIndex]+"contentDetails"+Constants.separators[sepIndex]+"specification", "at-3");  //update by increment this field on header changes    
            if(size > 1)
              con.setRequestProperty("data"+Constants.separators[sepIndex]+"contentDetails"+Constants.separators[sepIndex]+"list", "true");
            else
              con.setRequestProperty("data"+Constants.separators[sepIndex]+"contentDetails"+Constants.separators[sepIndex]+"list", "false");      
            con.setRequestProperty("data"+Constants.separators[sepIndex]+"contentDetails"+Constants.separators[sepIndex]+"listSize", String.valueOf(size));
            
            /* from AP 1.4 */
            if((lastToSendRecord.mBoxMac != null)&&(!lastToSendRecord.mBoxMac.equals("")))
            {
              Log.d("StoreAndForwardService", "postSecureData()--> box mac address: "+lastToSendRecord.mBoxMac);
              con.setRequestProperty("meta"+Constants.separators[sepIndex]+"sourceId", lastToSendRecord.mBoxMac);
            }
            else
              con.setRequestProperty("meta"+Constants.separators[sepIndex]+"sourceId", Utils.getDeviceAddress(getApplicationContext()));
            
            con.setRequestProperty("data"+Constants.separators[sepIndex]+"extendedSourceSessionId"+Constants.separators[sepIndex]+"seed", lastToSendRecord.mSourceSessionSeed);
            con.setRequestProperty("data"+Constants.separators[sepIndex]+"extendedSourceSessionId"+Constants.separators[sepIndex]+"number", String.valueOf(lastToSendRecord.mSourceSessionNumber));
            if((lastToSendRecord.mSemanticSessionSeed != null)&&(!lastToSendRecord.mSemanticSessionSeed.equals("")))
            {
              con.setRequestProperty("data"+Constants.separators[sepIndex]+"extendedSemanticSessionId"+Constants.separators[sepIndex]+"seed", lastToSendRecord.mSemanticSessionSeed);
              con.setRequestProperty("data"+Constants.separators[sepIndex]+"extendedSemanticSessionId"+Constants.separators[sepIndex]+"number", String.valueOf(lastToSendRecord.mSemanticSessionNumber));
            }
            
            con.setRequestProperty("data"+Constants.separators[sepIndex]+"contentDetails"+Constants.separators[sepIndex]+"typeVersion", "30"); //update by increment this field on header changes            
            /* end of from AP 1.4 */
            
            //******** geo header (for new version API V1)
            
            //add the right provider to header
            if(lastToSendRecord.mGpsProvider.equals(Constants.GPS_PROVIDERS[0])) //sensor box
            {
              con.setRequestProperty("geo"+Constants.separators[sepIndex]+"longitude", String.valueOf(lastToSendRecord.mBoxLon));
              con.setRequestProperty("geo"+Constants.separators[sepIndex]+"latitude", String.valueOf(lastToSendRecord.mBoxLat));
              con.setRequestProperty("geo"+Constants.separators[sepIndex]+"provider", lastToSendRecord.mGpsProvider);
              con.setRequestProperty("geo"+Constants.separators[sepIndex]+"timestamp", lastTsFormatted);
            }
            else if(lastToSendRecord.mGpsProvider.equals(Constants.GPS_PROVIDERS[1])) //phone
            {
              con.setRequestProperty("geo"+Constants.separators[sepIndex]+"longitude", String.valueOf(lastToSendRecord.mPhoneLon));
              con.setRequestProperty("geo"+Constants.separators[sepIndex]+"latitude", String.valueOf(lastToSendRecord.mPhoneLat));
              con.setRequestProperty("geo"+Constants.separators[sepIndex]+"accuracy", String.valueOf(lastToSendRecord.mPhoneAcc));
              con.setRequestProperty("geo"+Constants.separators[sepIndex]+"altitude", String.valueOf(lastToSendRecord.mPhoneAltitude)); //from AP 1.4
              con.setRequestProperty("geo"+Constants.separators[sepIndex]+"speed", String.valueOf(lastToSendRecord.mPhoneSpeed)); //from AP 1.4
              con.setRequestProperty("geo"+Constants.separators[sepIndex]+"bearing", String.valueOf(lastToSendRecord.mPhoneBear)); //from AP 1.4
              con.setRequestProperty("geo"+Constants.separators[sepIndex]+"provider", lastToSendRecord.mGpsProvider);
              con.setRequestProperty("geo"+Constants.separators[sepIndex]+"timestamp", lastTsFormatted);
            }
            else if(lastToSendRecord.mGpsProvider.equals(Constants.GPS_PROVIDERS[2])) //network
            {
              con.setRequestProperty("geo"+Constants.separators[sepIndex]+"longitude", String.valueOf(lastToSendRecord.mNetworkLon));
              con.setRequestProperty("geo"+Constants.separators[sepIndex]+"latitude", String.valueOf(lastToSendRecord.mNetworkLat));
              con.setRequestProperty("geo"+Constants.separators[sepIndex]+"accuracy", String.valueOf(lastToSendRecord.mNetworkAcc));
              con.setRequestProperty("geo"+Constants.separators[sepIndex]+"altitude", String.valueOf(lastToSendRecord.mNetworkAltitude)); //from AP 1.4
              con.setRequestProperty("geo"+Constants.separators[sepIndex]+"speed", String.valueOf(lastToSendRecord.mNetworkSpeed)); //from AP 1.4
              con.setRequestProperty("geo"+Constants.separators[sepIndex]+"bearing", String.valueOf(lastToSendRecord.mNetworkBear)); //from AP 1.4          
              con.setRequestProperty("geo"+Constants.separators[sepIndex]+"provider", lastToSendRecord.mGpsProvider);
              con.setRequestProperty("geo"+Constants.separators[sepIndex]+"timestamp", lastTsFormatted);
            }  
            
            //******** MAKING OF HTTP CONTENT (JSON) *************
            
            //writing string content as an array of json object
            StringBuilder sb = new StringBuilder();
          sb.append("[");
          
          JSONObject object = new JSONObject();
          
          try 
          {
            object.put("timestamp", lastTimestamp);
            
            JSONArray locations = new JSONArray();
            
            //sensor box gps data
            if(lastToSendRecord.mBoxLat != 0)
            {
              JSONObject boxLocation = new JSONObject();
              boxLocation.put("latitude", lastToSendRecord.mBoxLat);
              boxLocation.put("longitude", lastToSendRecord.mBoxLon);
              //boxLocation.put("accuracy", "null");
              //boxLocation.put("altitude", "null");
              //boxLocation.put("speed", "null");
              //boxLocation.put("bearing", "null");
              boxLocation.put("provider", Constants.GPS_PROVIDERS[0]);
              boxLocation.put("timestamp", lastToSendRecord.mBoxTimestamp);
              
              locations.put(0, boxLocation);
            }
            
            //phone gps data
            if(lastToSendRecord.mPhoneLat != 0)
            {
              JSONObject phoneLocation = new JSONObject();
              phoneLocation.put("latitude", lastToSendRecord.mPhoneLat);
              phoneLocation.put("longitude", lastToSendRecord.mPhoneLon);
              phoneLocation.put("accuracy", lastToSendRecord.mAccuracy);
              phoneLocation.put("altitude", lastToSendRecord.mPhoneAltitude);
              phoneLocation.put("speed", lastToSendRecord.mPhoneSpeed);
              phoneLocation.put("bearing", lastToSendRecord.mPhoneBear);
              phoneLocation.put("provider", Constants.GPS_PROVIDERS[1]);
              phoneLocation.put("timestamp", lastToSendRecord.mPhoneTimestamp);
              
              locations.put(1, phoneLocation);
            }
            
            //network gps data
            if(lastToSendRecord.mNetworkLat != 0)
            {
              JSONObject netLocation = new JSONObject();
              netLocation.put("latitude", lastToSendRecord.mNetworkLat);
              netLocation.put("longitude", lastToSendRecord.mNetworkLon);
              netLocation.put("accuracy", lastToSendRecord.mNetworkAcc);
              netLocation.put("altitude", lastToSendRecord.mNetworkAltitude);
              netLocation.put("speed", lastToSendRecord.mNetworkSpeed);
              netLocation.put("bearing", lastToSendRecord.mNetworkBear);        
              netLocation.put("provider", Constants.GPS_PROVIDERS[2]);
              netLocation.put("timestamp", lastToSendRecord.mNetworkTimestamp);

              locations.put(2, netLocation);
            }
            
            object.put("locations", locations);
          }
          catch (JSONException e) 
          {
            e.printStackTrace();
          }
          
          String concatOfTags = "";
          
          //put the tags of all records in concatOfTags string
          for(int j = 0; j < recordsWithTags.size(); j++)
          {
            if((recordsWithTags.get(j).mUserData1 != null)&&(!recordsWithTags.get(j).mUserData1.equals("")))
              concatOfTags += recordsWithTags.get(j).mUserData1+" ";
          }
          Log.d("StoreAndForwardService", "postSecureTags()--> concat of tags: " +concatOfTags);
          
          try 
          {
            String[] tags = concatOfTags.split(" ");
            JSONArray tagsArray = new JSONArray();
            if((tags != null)&&(tags.length > 0))
            {
              for(int k = 0; k < tags.length; k++)
              {
                if(!tags[k].equals(""))
                  tagsArray.put(k, tags[k]);
              }
            }
            object.put("tags", tagsArray);
            //object.put("tags_cause", null);
            //object.put("tags_location", null);
            //object.put("tags_perception", null);
          } 
          catch (JSONException e) 
          {
            e.printStackTrace();
          }
          sb.append(object.toString());  
          sb.append("]");        

          Log.d("StoreAndForwardService", "postSecureTags()--> json to string: " +sb.toString());
          
          //compress json content into byte array entity
          byte[] contentGzippedBytes = zipStringToBytes(sb.toString());
  
            //write json compressed content into output stream
          OutputStream outputStream = con.getOutputStream();
          outputStream.write(contentGzippedBytes);
          outputStream.flush();
          outputStream.close();
          
          int responseCode = con.getResponseCode();
          
          Log.d("StoreAndForwardService", "postSecureTags()--> response code: " +responseCode);

          sb = null;

          if(responseCode == Constants.STATUS_OK)
          {
            Log.d("StoreAndForwardService", "postSecureTags()--> STATUS OK");
            mDbManager.deleteRecordsWithTagsBySessionId(sessionId);          
          }
          else
            Log.d("StoreAndForwardService", "postSecureTags()--> status error code: "+responseCode);          
        }
        else
          Log.d("StoreAndForwardService", "postTags()--> no tags to send");
      }
    }
  }    
  
   public static byte [] zipStringToBytes (String input) throws IOException
   {
       ByteArrayOutputStream bos = new ByteArrayOutputStream ();
       //BufferedOutputStream buffs = new BufferedOutputStream (new GZIPOutputStream (bos));
       
       //use PrintStream to avod converting a String into byte array, that can cause out of memory error
       final PrintStream printStream = new PrintStream(new GZIPOutputStream (bos));
       printStream.print(input);
       printStream.close();
       
       //buffs.write (input. getBytes ());
       //buffs.close ();
       
       byte [] retval = bos.toByteArray ();
       bos.close ();
       return retval;
   } 
   
   //check if actual access token is valid: creation time + expiresIn field must be > actual time
   public boolean checkIfAccessTokenIsValid()
   {     
     long creationTime = Utils.getCreationTime(getApplicationContext());
     if(creationTime < 0)
       return false;
     
     long expiresIn = Utils.getExpiresIn(getApplicationContext());
     if(expiresIn < 0)
       return false;
     expiresIn -= 1000;
     
     long actualTime = System.currentTimeMillis() / 1000; //not valid date?
     if(actualTime <= 0)
        return false;
     
     //Log.d("StoreAndForwardService", "checkIfAccessTokenIsValid()--> creationTime: "+creationTime+" expiresIn: "+expiresIn+" actualTime: "+actualTime);
     
     if(creationTime + expiresIn > actualTime)   
     {
       Log.d("StoreAndForwardService", "checkIfAccessTokenIsValid()--> STILL VALID ACCESS TOKEN - creationTime + expiresIn "+(creationTime+expiresIn)+" actualTime: "+actualTime);
       return true;
     }
       else
       return false;
   }
   
   //check if actual access token is valid: creation time + expiresIn field must be > actual time
   public boolean checkIfAccessTokenIsValidForClient()
   {     
     long creationTime = Utils.getCreationTimeForClient(getApplicationContext());
     if(creationTime < 0)
       return false;
     
     long expiresIn = Utils.getExpiresInForClient(getApplicationContext());
     if(expiresIn < 0)
       return false;
     expiresIn -= 1000;
     
     long actualTime = System.currentTimeMillis() / 1000; //not valid date?
     if(actualTime <= 0)
       return false;
     
     //Log.d("StoreAndForwardService", "checkIfAccessTokenIsValidForClient()--> creationTime: "+creationTime+" expiresIn: "+expiresIn+" actualTime: "+actualTime);
     
     if(creationTime + expiresIn > actualTime)   
     {
       Log.d("StoreAndForwardService", "checkIfAccessTokenIsValidForClient()--> STILL VALID ACCESS TOKEN - creationTime + expiresIn "+(creationTime+expiresIn)+" actualTime: "+actualTime);
       return true;
     }
       else
       return false;
   }
}




Java Source Code List

android.UnusedStub.java
org.csp.everyaware.ColorHelper.java
org.csp.everyaware.Constants.java
org.csp.everyaware.Credits.java
org.csp.everyaware.ExtendedLatLng.java
org.csp.everyaware.Installation.java
org.csp.everyaware.KmlParser.java
org.csp.everyaware.ManageAccount.java
org.csp.everyaware.Options.java
org.csp.everyaware.Start.java
org.csp.everyaware.Utils.java
org.csp.everyaware.bluetooth.BluetoothBroadcastReceiver.java
org.csp.everyaware.bluetooth.BluetoothHistoryManager.java
org.csp.everyaware.bluetooth.BluetoothManager.java
org.csp.everyaware.bluetooth.BluetoothObject.java
org.csp.everyaware.db.AnnotatedRecord.java
org.csp.everyaware.db.DbManager.java
org.csp.everyaware.db.MapCluster.java
org.csp.everyaware.db.MarkerRecord.java
org.csp.everyaware.db.Record.java
org.csp.everyaware.db.SemanticSessionDetails.java
org.csp.everyaware.db.Track.java
org.csp.everyaware.facebooksdk.AsyncFacebookRunner.java
org.csp.everyaware.facebooksdk.DialogError.java
org.csp.everyaware.facebooksdk.FacebookError.java
org.csp.everyaware.facebooksdk.Facebook.java
org.csp.everyaware.facebooksdk.FbDialog.java
org.csp.everyaware.facebooksdk.Util.java
org.csp.everyaware.fragments.FragmentWizardStep0.java
org.csp.everyaware.fragments.FragmentWizardStep1.java
org.csp.everyaware.fragments.FragmentWizardStep2.java
org.csp.everyaware.gps.GpsTrackingService.java
org.csp.everyaware.internet.FacebookManager.java
org.csp.everyaware.internet.StoreAndForwardService.java
org.csp.everyaware.internet.TwitterLogin.java
org.csp.everyaware.internet.TwitterManager.java
org.csp.everyaware.offline.Graph.java
org.csp.everyaware.offline.Map.java
org.csp.everyaware.offline.MyTracks.java
org.csp.everyaware.offline.Tabs.java
org.csp.everyaware.tabactivities.Graph.java
org.csp.everyaware.tabactivities.Map.java
org.csp.everyaware.tabactivities.Monitor.java
org.csp.everyaware.tabactivities.Tabs.java