content: /*----------------------------------------------------------------------------+
   *|                                                                           |
   *|                          Android's Hooker                                 |
   *|                                                                           |
   *+---------------------------------------------------------------------------+
   *| Copyright (C) 2011 Georges Bossert and Dimitri Kirchner                   |
   *| 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/>.      |
   *+---------------------------------------------------------------------------+
   *| @url      : http://www.amossys.fr                                         |
   *| @contact  : android-hooker@amossys.fr                                     |
   *| @sponsors : Amossys, http://www.amossys.fr                                |
   *+---------------------------------------------------------------------------+
   */
  
  package com.amossys.hooker.common;
  
  import java.util.AbstractMap;
  import java.util.ArrayList;
  import java.util.Calendar;
  import java.util.HashMap;
  import java.util.List;
  import java.util.Map;
  import java.util.UUID;
  import java.util.Map.Entry;
  
  import org.json.JSONArray;
  import org.json.JSONException;
  import org.json.JSONObject;
  
  import com.amossys.hooker.SubstrateMain;
  
  import android.os.Parcel;
  import android.os.Parcelable;
  
  /**
   * InterceptEvent class contains data we want to extract.
   * This class implements Parcelable so we can send it to our Service.
   * @author Dimitri Kirchner & Georges Bossert.
   *
   */
  public class InterceptEvent implements Parcelable {
  
    // unique ID of the event
    private UUID idEvent;
    // experiment iD
    private String IDXP;
    // creation date of the event
    private long timestamp;
    // relative creation date of the event
    private long relativeTimestamp;
    // name of the hooker who created this event
    private String hookerName;
    // 0: Nothing related personal info,
    // 1: Reading personal info
    // 2: Writing personal info
    private int intrusiveLevel;
    // instance id
    private int instanceID;  
    // name of the package in which we hooked this event
    private String packageName;
    // name of the class we attached on
    private String className;
    // name of the method we attached on
    private String methodName;
    // list of parameters used to class this method
    private List<Entry<String, String>> parameters = new ArrayList<Entry<String, String>>();
    // returned values of this method
    private Entry<String, String> returns;
    // optional data added to this event
    private Map<String, String> data = new HashMap<String, String>();
  
    /**
     * Constructor which will be used to read in the Parcel.
     * @param in
     */
  	private InterceptEvent(Parcel in) {
  		idEvent = UUID.fromString(in.readString());
  		IDXP = in.readString();
  		timestamp = in.readLong();
  		relativeTimestamp = in.readLong();
  		hookerName = in.readString();
  		intrusiveLevel = in.readInt();
  		instanceID = in.readInt();
  		packageName = in.readString();
  		className = in.readString();
  		methodName = in.readString();
  
  		readParametersList(in);
  		readReturnsEntry(in);
  		readDataMap(in);
  	}
  	
  	/**
  	 * Write to the parcel to build the class.
  	 */
  	@Override
  	public void writeToParcel(Parcel out, int flags) {
  		out.writeString(idEvent.toString());
  		out.writeString(IDXP);
  		out.writeLong(timestamp);
  		out.writeLong(relativeTimestamp);
  		out.writeString(hookerName);
  		out.writeInt(intrusiveLevel);
  		out.writeInt(instanceID);
  		out.writeString(packageName);
  		out.writeString(className);
  		out.writeString(methodName);
  		
  		writeParametersList(out);
  		writeReturnsEntry(out);
  		writeDataMap(out);
  	}
  	
  	/**
  	 * Read and write functions to handle the parameters list.
  	 * @param in
  	 */
  	private void readParametersList(Parcel in){
  		int size = in.readInt();
  		if (size!=0){
  			for (int i=0; i<size; i++) {
  				String key=in.readString();
  				String value=in.readString();
  			  parameters.add(new AbstractMap.SimpleEntry<String, String>(key, value));
  			}
  		}
  	}
  	private void writeParametersList(Parcel out){
  		if (parameters==null){
  			out.writeInt(0);
  		}
  		else{
  			out.writeInt(parameters.size());
  			for (Entry<String, String> entry : parameters) {
  			  out.writeString(entry.getKey());
  			  out.writeString(entry.getValue());
  			}
  		}
  	}
  	
  	/**
  	 * Read and write function to handle the returns list.
  	 * @param in
  	 */
  	private void readReturnsEntry(Parcel in){
  		int tmp = in.readInt();
  		if (tmp==0){
  			
  		}
  		else{
  			String key=in.readString();
  			String value=in.readString();
  			returns = new AbstractMap.SimpleEntry<String, String>(key, value);
  		}
  	}
  	private void writeReturnsEntry(Parcel out){
  		if (returns==null){
  			out.writeInt(0);
  		}
  		else{
  			out.writeInt(1);
  			out.writeString(returns.getKey());
  			out.writeString(returns.getValue());
  		}
  	}
  	
  	/**
  	 * Read and write function to handle the data map.
  	 * @param in
  	 */
  	private void readDataMap(Parcel in){
  		int size = in.readInt();
  		if(size!=0){
  			for (int i=0; i<size; i++){
  				String key=in.readString();
  				String value=in.readString();
  				data.put(key, value);
  			}
  		}
  	}
  	private void writeDataMap(Parcel out){
  		if (data==null){
  			out.writeInt(0);
  		}
  		else{
  			out.writeInt(data.size());
  			for (String key : data.keySet()){
  				out.writeString(key);
  				out.writeString(data.get(key));
  			}
  		}
  	}
  
  	/**
  	 * Creator necessary to build a Parcelable class
  	 */
  	public static final Parcelable.Creator<InterceptEvent> CREATOR = new Parcelable.Creator<InterceptEvent>() {
  		public InterceptEvent createFromParcel(Parcel in) {
  			return new InterceptEvent(in);
  		}
  
  		public InterceptEvent[] newArray(int size) {
  			return new InterceptEvent[size];
  		}
  	};
  
  	
  	/**
     * Simple constructor for event.
     */
    public InterceptEvent(String hookerName, int intrusiveLevel, int instanceId, String packageName, String className, String methodName) {
      this(buildRandomUUID(), 0l, 0, hookerName, intrusiveLevel, instanceId, packageName, 
          className, methodName, null, null, null);
    }
    
    
    /**
     * Detailed constructor.
     */
    public InterceptEvent(UUID idEvent, long timestamp, long relativeTimestamp, String hookerName, int intrusiveLevel, 
    		int instanceId, String packageName,
        String className, String methodName, List<Entry<String, String>> parameters,
        Entry<String, String> returns, Map<String, String> data) {
    	
      super();
      this.idEvent = idEvent;
      if (timestamp == 0) {
        try {
          timestamp = Calendar.getInstance().getTime().getTime();
        }catch(Exception e) {
          timestamp = 0;
          SubstrateMain.log("Error while computing the current timestamp...", e);
        }
      }
      this.timestamp = timestamp;
      this.relativeTimestamp = relativeTimestamp;
      this.hookerName = hookerName;
      this.intrusiveLevel = intrusiveLevel;
      this.instanceID = instanceId;
      this.packageName = packageName;
      this.className = className;
      this.methodName = methodName;
      this.parameters = parameters;
      this.returns = returns;
      this.data = data;
    }
  	
    /**
     * @return a random UUID number.
     * @todo When the system starts, we do not have any class UUID, so we must construct ourselves the UUID.
     * In order to do this better, we have to implement a small random UUID generator.
     */
    private static UUID buildRandomUUID() {
      try {   
        return UUID.randomUUID();
      } catch (Exception e) {
        return UUID.fromString("00000000-1111-2222-3333-444444444444");
      }
    }
  
  	@Override
  	public int describeContents() {
  		return 0;
  	}
  
  	
  	/**
     * Register a new parameter
     * @param type: type of the parameter
     * @param value: value of the parameter
     */
    public void addParameter(String type, String value) {
      if (this.parameters == null) {
        this.parameters = new ArrayList<Map.Entry<String,String>>();
      }
      this.parameters.add(new AbstractMap.SimpleEntry<String, String>(type, value));
    }
  
    
  
    /**
     * @return the idEvent
     */
    public UUID getIdEvent() {
      return idEvent;
    }
  
  
    /**
     * @param idEvent the idEvent to set
     */
    public void setIdEvent(UUID idEvent) {
      this.idEvent = idEvent;
    }
  
  
    /**
     * @return the timestamp
     */
    public long getTimestamp() {
      return timestamp;
    }
  
  
    /**
     * @param timestamp the timestamp to set
     */
    public void setTimestamp(long timestamp) {
      this.timestamp = timestamp;
    }
  
  
    /**
     * @return the relativeTimestamp
     */
    public long getRelativeTimestamp() {
      return relativeTimestamp;
    }
  
  
    /**
     * @param relativeTimestamp the relativeTimestamp to set
     */
    public void setRelativeTimestamp(long relativeTimestamp) {
      this.relativeTimestamp = relativeTimestamp;
    }
  
  
    /**
     * @return the hookerName
     */
    public String getHookerName() {
      return hookerName;
    }
  
  
    /**
     * @param hookerName the hookerName to set
     */
    public void setHookerName(String hookerName) {
      this.hookerName = hookerName;
    }
  
  
    /**
     * @return the intrusiveLevel
     */
    public int getIntrusiveLevel() {
      return intrusiveLevel;
    }
  
  
    /**
     * @param intrusiveLevel the intrusiveLevel to set
     */
    public void setIntrusiveLevel(int intrusiveLevel) {
      this.intrusiveLevel = intrusiveLevel;
    }
  
    
  
    /**
     * @return the packageName
     */
    public String getPackageName() {
      return packageName;
    }
  
  
    /**
     * @param packageName the packageName to set
     */
    public void setPackageName(String packageName) {
      this.packageName = packageName;
    }
  
  
    /**
     * @return the className
     */
    public String getClassName() {
      return className;
    }
  
  
    /**
     * @param className the className to set
     */
    public void setClassName(String className) {
      this.className = className;
    }
  
  
    /**
     * @return the methodName
     */
    public String getMethodName() {
      return methodName;
    }
  
  
    /**
     * @param methodName the methodName to set
     */
    public void setMethodName(String methodName) {
      this.methodName = methodName;
    }
  
  
    /**
     * @return the parameters
     */
    public List<Entry<String, String>> getParameters() {
      return parameters;
    }
  
  
    /**
     * @param parameters the parameters to set
     */
    public void setParameters(List<Entry<String, String>> parameters) {
      this.parameters = parameters;
    }
  
  
    /**
     * @return the returns
     */
    public Entry<String, String> getReturns() {
      return returns;
    }
  
  
    /**
     * @param returns the returns to set
     */
    public void setReturns(Entry<String, String> returns) {
      this.returns = returns;
    }
    
    /**
     * @param name
     * @param string
     */
    public void setReturns(String type, String value) {
      this.returns = new AbstractMap.SimpleEntry<String, String>(type, value);
      
    }
  
  
    /**
     * @return the data
     */
    public Map<String, String> getData() {
      return data;
    }
  
  
    /**
     * @param data the data to set
     */
    public void setData(Map<String, String> data) {
      this.data = data;
    }
  
  
    /**
     * @return the iDXP
     */
    public String getIDXP() {
      return IDXP;
    }
  
    /**
     * @return the instanceID
     */
    public int getInstanceID() {
      return instanceID;
    }
  
  
    /**
     * @param instanceID the instanceID to set
     */
    public void setInstanceID(int instanceID) {
      this.instanceID = instanceID;
    }
  
  
    /**
     * @param iDXP the iDXP to set
     */
    public void setIDXP(String iDXP) {
      IDXP = iDXP;
    }
  
  	@Override
  	public String toString() {
  		StringBuilder builder = new StringBuilder();
  		builder.append("InterceptEvent [idEvent=");
  		builder.append(idEvent);
  		builder.append(", IDXP=");
  		builder.append(this.IDXP);
  		builder.append(", timestamp=");
  		builder.append(timestamp);
  		builder.append(", hookerName=");
  		builder.append(hookerName);
  		builder.append(", intrusiveLevel=");
  		builder.append(intrusiveLevel);
  		builder.append(", instanceID=");
  		builder.append(instanceID);
  		builder.append(", packageName=");
  		builder.append(packageName);
  		builder.append(", className=");
  		builder.append(className);
  		builder.append(", methodName=");
  		builder.append(methodName);
  		builder.append("]");
  		return builder.toString();
  	}
    
    
    /**
     * @return
     */
    public String toJson() {
      JSONObject object = new JSONObject();
  
      try {
  //      object.put("IdEvent", this.getIdEvent().toString());
  
        object.put("Timestamp", this.getTimestamp());
        object.put("RelativeTimestamp", this.getRelativeTimestamp());
        object.put("HookerName", this.getHookerName());
        object.put("IntrusiveLevel", this.getIntrusiveLevel());
        object.put("InstanceID", this.getInstanceID());
        object.put("PackageName", this.getPackageName());
        
        object.put("ClassName", this.getClassName());
        object.put("MethodName", this.getMethodName());
  
        JSONArray parameters = new JSONArray();
        if (this.getParameters() != null) {
          for (Entry<String, String> parameter : this.getParameters()) {
            JSONObject jsonParameter = new JSONObject();
            jsonParameter.put("ParameterType", parameter.getKey());
            jsonParameter.put("ParameterValue", parameter.getValue());
            parameters.put(jsonParameter);
          }
        }
        object.put("Parameters", parameters);
  
        JSONObject returns = new JSONObject();
        if (this.getReturns() != null) {
          returns.put("ReturnType", this.getReturns().getKey());
          returns.put("ReturnValue", this.getReturns().getValue());
        }
        object.put("Return", returns);
  
  
        JSONArray data = new JSONArray();
        if (this.getData() != null) {
          for (String dataName : this.getData().keySet()) {
            if (dataName != null && this.getData().get(dataName) != null) {
              JSONObject dataP = new JSONObject();
              dataP.put("DataName", dataName);
              dataP.put("DataValue", this.getData().get(dataName));
            }
          }
        }
        object.put("Data", data);
      } catch (JSONException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
      }
      return object.toString();
    }
  }
  