content: package edu.berkeley.cs.amplab.carat.android.sampling;
  
  import java.io.BufferedReader;
  import java.io.ByteArrayInputStream;
  import java.io.File;
  import java.io.FileInputStream;
  import java.io.FileNotFoundException;
  import java.io.IOException;
  import java.io.InputStreamReader;
  import java.io.RandomAccessFile;
  import java.lang.ref.WeakReference;
  import java.security.MessageDigest;
  import java.security.NoSuchAlgorithmException;
  import java.security.PublicKey;
  import java.security.cert.CertificateException;
  import java.security.cert.CertificateFactory;
  import java.security.cert.X509Certificate;
  import java.security.interfaces.DSAPublicKey;
  import java.security.interfaces.RSAPublicKey;
  import java.text.SimpleDateFormat;
  import java.util.ArrayList;
  import java.util.Calendar;
  import java.util.Date;
  import java.util.Enumeration;
  import java.util.HashMap;
  import java.util.HashSet;
  import java.util.LinkedList;
  import java.util.List;
  import java.util.Map;
  import java.util.Map.Entry;
  import java.util.Properties;
  import java.util.Set;
  import java.util.TimeZone;
  
  import android.app.Activity;
  import android.app.ActivityManager;
  import android.app.ActivityManager.RunningAppProcessInfo;
  import android.app.ActivityManager.RunningServiceInfo;
  import android.content.ContentResolver;
  import android.content.Context;
  import android.content.Intent;
  import android.content.SharedPreferences;
  import android.content.SharedPreferences.Editor;
  import android.content.pm.ApplicationInfo;
  import android.content.pm.PackageInfo;
  import android.content.pm.PackageManager;
  import android.content.pm.PackageManager.NameNotFoundException;
  import android.content.pm.Signature;
  import android.database.Cursor;
  import android.location.Criteria;
  import android.location.GpsStatus;
  import android.location.Location;
  import android.location.LocationManager;
  import android.net.ConnectivityManager;
  import android.net.NetworkInfo;
  import android.net.wifi.WifiInfo;
  import android.net.wifi.WifiManager;
  import android.os.BatteryManager;
  import android.os.SystemClock;
  import android.preference.PreferenceManager;
  import android.provider.Settings;
  import android.provider.Settings.Secure;
  import android.provider.Settings.SettingNotFoundException;
  import android.telephony.CellLocation;
  import android.telephony.TelephonyManager;
  import android.telephony.cdma.CdmaCellLocation;
  import android.telephony.gsm.GsmCellLocation;
  import android.util.Log;
  
  import com.flurry.android.FlurryAgent;
  
  import edu.berkeley.cs.amplab.carat.android.CaratApplication;
  import edu.berkeley.cs.amplab.carat.android.Constants;
  import edu.berkeley.cs.amplab.carat.thrift.BatteryDetails;
  import edu.berkeley.cs.amplab.carat.thrift.CallMonth;
  import edu.berkeley.cs.amplab.carat.thrift.CellInfo;
  import edu.berkeley.cs.amplab.carat.thrift.CpuStatus;
  import edu.berkeley.cs.amplab.carat.thrift.Feature;
  import edu.berkeley.cs.amplab.carat.thrift.NetworkDetails;
  import edu.berkeley.cs.amplab.carat.thrift.ProcessInfo;
  import edu.berkeley.cs.amplab.carat.thrift.Sample;
  
  /**
   * Library class for methods that obtain information about the phone that is
   * running Carat.
   * 
   * @author Eemil Lagerspetz
   * 
   */
  public final class SamplingLibrary {
  	private static final boolean collectSignatures = true;
  	public static final String SIG_SENT = "sig-sent:";
  	public static final String SIG_SENT_256 = "sigs-sent:";
  	public static final String INSTALLED = "installed:";
  	public static final String REPLACED = "replaced:";
  	public static final String UNINSTALLED = "uninstalled:";
  	// Disabled or turned off applications will be scheduled for reporting using this prefix
  	public static final String DISABLED = "disabled:";
  
  	private static final int READ_BUFFER_SIZE = 2 * 1024;
  	// Network status constants
  	public static String NETWORKSTATUS_DISCONNECTED = "disconnected";
  	public static String NETWORKSTATUS_DISCONNECTING = "disconnecting";
  	public static String NETWORKSTATUS_CONNECTED = "connected";
  	public static String NETWORKSTATUS_CONNECTING = "connecting";
  	// Network type constants
  	public static String TYPE_UNKNOWN = "unknown";
  	// Data State constants
  	public static String DATA_DISCONNECTED = NETWORKSTATUS_DISCONNECTED;
  	public static String DATA_CONNECTING = NETWORKSTATUS_CONNECTING;
  	public static String DATA_CONNECTED = NETWORKSTATUS_CONNECTED;
  	public static String DATA_SUSPENDED = "suspended";
  	// Data Activity constants
  	public static String DATA_ACTIVITY_NONE = "none";
  	public static String DATA_ACTIVITY_IN = "in";
  	public static String DATA_ACTIVITY_OUT = "out";
  	public static String DATA_ACTIVITY_INOUT = "inout";
  	public static String DATA_ACTIVITY_DORMANT = "dormant";
  	// Wifi State constants
  	public static String WIFI_STATE_DISABLING = "disabling";
  	public static String WIFI_STATE_DISABLED = "disabled";
  	public static String WIFI_STATE_ENABLING = "enabling";
  	public static String WIFI_STATE_ENABLED = "enabled";
  	public static String WIFI_STATE_UNKNOWN = "unknown";
  	// Call state constants
  	public static String CALL_STATE_IDLE = "idle";
  	public static String CALL_STATE_OFFHOOK = "offhook";
  	public static String CALL_STATE_RINGING = "ringing";
  
  	// Mobile network constants
  	/*
  	 * we cannot find network types:EVDO_B,LTE,EHRPD,HSPAP from TelephonyManager
  	 * now
  	 */
  	public static String NETWORK_TYPE_UNKNOWN = "unknown";
  	public static String NETWORK_TYPE_GPRS = "gprs";
  	public static String NETWORK_TYPE_EDGE = "edge";
  	public static String NETWORK_TYPE_UMTS = "utms";
  	public static String NETWORK_TYPE_CDMA = "cdma";
  	public static String NETWORK_TYPE_EVDO_0 = "evdo_0";
  	public static String NETWORK_TYPE_EVDO_A = "evdo_a";
  	public static String NETWORK_TYPE_EVDO_B = "evdo_b";
  	public static String NETWORK_TYPE_1xRTT = "1xrtt";
  	public static String NETWORK_TYPE_HSDPA = "hsdpa";
  	public static String NETWORK_TYPE_HSUPA = "hsupa";
  	public static String NETWORK_TYPE_HSPA = "hspa";
  	public static String NETWORK_TYPE_IDEN = "iden";
  	public static String NETWORK_TYPE_LTE = "lte";
  	public static String NETWORK_TYPE_EHRPD = "ehrpd";
  	public static String NETWORK_TYPE_HSPAP = "hspap";
  
  	private static final int EVDO_B = 12;
  	private static final int LTE = 13;
  	private static final int EHRPD = 14;
  	private static final int HSPAP = 15;
  
  	// Phone type constants
  	public static String PHONE_TYPE_CDMA = "cdma";
  	public static String PHONE_TYPE_GSM = "gsm";
  	// public static String PHONE_TYPE_SIP="sip";
  	public static String PHONE_TYPE_NONE = "none";
  
  	public static double startLatitude = 0;
  	public static double startLongitude = 0;
  	public static double distance = 0;
  
  	private static final String STAG = "getSample";
  	// private static final String TAG="FeaturesPowerConsumption";
  
  	public static final int UUID_LENGTH = 16;
  
  	private static double lastBatteryLevel;
  	private static double currentBatteryLevel;
  
  	// we might not be able to read the current battery level at the first run
  	// of Carat.
  	// so it might be zero until we get the non-zero value from the intent
  	// (BatteryManager.EXTRA_LEVEL & BatteryManager.EXTRA_SCALE)
  
  	/** Library class, prevent instantiation */
  	private SamplingLibrary() {
  	}
  
  	/**
  	 * Returns a randomly generated unique identifier that stays constant for
  	 * the lifetime of the device. (May change if wiped). This is probably our
  	 * best choice for a UUID across the Android landscape, since it is present
  	 * on both phones and non-phones.
  	 * 
  	 * @return a String that uniquely identifies this device.
  	 */
  	public static String getAndroidId(Context c) {
  		return Secure.getString(c.getContentResolver(), Secure.ANDROID_ID);
  	}
  
  	public static String getUuid(Context c) {
  		return getTimeBasedUuid(c, false);
  	}
  
  	public static String getTimeBasedUuid(Context c) {
  		return getTimeBasedUuid(c, true);
  	}
  
  	public static double readLastBatteryLevel() {
  		return lastBatteryLevel;
  	}
  
  	public static void setLastBatteryLevel(double level) {
  		SamplingLibrary.lastBatteryLevel = level;
  	}
  
  	public static double getLastBatteryLevel(Context context) {
  		return lastBatteryLevel;
  	}
  
  	public static double getCurrentBatteryLevel() {
  		return currentBatteryLevel;
  	}
  
  	public static void setCurrentBatteryLevel(double level) {
  		SamplingLibrary.currentBatteryLevel = level;
  	}
  	
  	/**
  	 * 
  	 * Take in currentLevel and scale as doubles to avoid loss of precision issues.
  	 * Note that Carat stores battery level as a value between 0 and 1, e.g. 0.45 for 45%.
  	 * @param currentLevel Current battery level, usually in percent.
  	 * @param scale Battery scale, usually 100.0.
  	 */
  	public static void setCurrentBatteryLevel(double currentLevel, double scale) {
  		/* we should multiply the result of the division below by 100.0 to get the battery level 
  		 * in the scale of 0-100, but since the previous samples in our server's dataset are in the scale of 0.00-1.00, 
  		 * we omit the multiplication. */
  		double level = currentLevel / scale;
  		/*
  		 * whenever we get these two arguments (extras from the intent:
  		 * EXTRA_LEVEL & EXTRA_SCALE), it doens't necessarily mean that a battery
  		 * percentage change has happened. Check the comments in the
  		 * broadcast receiver (sampler).
  		 */
  		if (level != getCurrentBatteryLevel()) {
  			setCurrentBatteryLevel(level);
  			// Log.d("SamplingLibrary.setCurrentBatteryLevel()", "currentBatteryLevel="
  			//		+ getCurrentBatteryLevel());
  		}
  	}
  
  	/**
  	 * Generate a time-based, random identifier.
  	 * 
  	 * @param c
  	 *            the app's Context
  	 * @return a time-based, random identifier.
  	 */
  	public static String getTimeBasedUuid(Context c, boolean includeTimestamp) {
  		String aID = getAndroidId(c);
  		String wifiMac = getWifiMacAddress(c);
  		String devid = getDeviceId(c);
  		String concat = "";
  		if (aID != null)
  			concat = aID;
  		else
  			concat = "0000000000000000";
  		if (wifiMac != null)
  			concat += wifiMac;
  		else
  			concat += "00:00:00:00:00:00";
  
  		// IMEI is 15 characters, decimal, while MEID is 14 characters, hex. Add
  		// a space if length is less than 15:
  		if (devid != null) {
  			concat += devid;
  			if (devid.length() < 15)
  				concat += " ";
  		} else
  			concat += "000000000000000";
  		if (includeTimestamp) {
  			long timestamp = System.currentTimeMillis();
  			concat += timestamp;
  		}
  
  		// Log.d(STAG,
  		// "AID="+aID+" wifiMac="+wifiMac+" devid="+devid+" rawUUID=" +concat );
  		try {
  			MessageDigest md = MessageDigest.getInstance("SHA-512");
  			md.update(concat.getBytes());
  			byte[] mdbytes = md.digest();
  			StringBuilder hexString = new StringBuilder();
  			for (int i = 0; i < mdbytes.length; i++) {
  				String hx = Integer.toHexString(0xFF & mdbytes[i]);
  				if (hx.equals("0"))
  					hexString.append("00");
  				else
  					hexString.append(hx);
  			}
  			String uuid = hexString.toString().substring(0, UUID_LENGTH);
  			// FlurryAgent.logEvent("ANDROID_ID=" + aID +" UUID=" + uuid);
  			return uuid;
  		} catch (NoSuchAlgorithmException e) {
  			e.printStackTrace();
  			return aID;
  		}
  	}
  
  	/**
  	 * Returns the model of the device running Carat, for example "sdk" for the
  	 * emulator, Galaxy Nexus for Samsung Galaxy Nexus.
  	 * 
  	 * @return the model of the device running Carat, for example "sdk" for the
  	 *         emulator, Galaxy Nexus for Samsung Galaxy Nexus.
  	 */
  	public static String getModel() {
  		return android.os.Build.MODEL;
  	}
  
  	/**
  	 * Returns the manufacturer of the device running Carat, for example
  	 * "google" or "samsung".
  	 * 
  	 * @return the manufacturer of the device running Carat, for example
  	 *         "google" or "samsung".
  	 */
  	public static String getManufacturer() {
  		return android.os.Build.MANUFACTURER;
  	}
  
  	/**
  	 * Returns the OS version of the device running Carat, for example 2.3.3 or
  	 * 4.0.2.
  	 * 
  	 * @return the OS version of the device running Carat, for example 2.3.3 or
  	 *         4.0.2.
  	 */
  	public static String getOsVersion() {
  		return android.os.Build.VERSION.RELEASE;
  	}
  
  	/**
  	 * Returns the product name.
  	 * 
  	 * @return the product name.
  	 */
  	public static String getProductName() {
  		return android.os.Build.PRODUCT;
  	}
  
  	/**
  	 * Returns the kernel version, e.g. 3.4-1101.
  	 * 
  	 * @return the kernel version, e.g. 3.4-1101.
  	 */
  	public static String getKernelVersion() {
  		return System.getProperty("os.version", TYPE_UNKNOWN);
  	}
  
  	/**
  	 * Returns the build serial number. May only work for 2.3 and up.
  	 * 
  	 * @return the build serial number.
  	 */
  	public static String getBuildSerial() {
  		// return android.os.Build.Serial;
  		return System.getProperty("ro.serial", TYPE_UNKNOWN);
  	}
  
  	/**
  	 * Print all system properties for debugging.
  	 * 
  	 */
  	public static void printAllProperties() {
  		Properties list = System.getProperties();
  		Enumeration<Object> keys = list.keys();
  		while (keys.hasMoreElements()) {
  			String k = (String) keys.nextElement();
  			String v = list.getProperty(k);
  			Log.d("PROPS", k + "=" + v);
  		}
  	}
  
  	/**
  	 * Returns the brand for which the device is customized, e.g. Verizon.
  	 * 
  	 * @return the brand for which the device is customized, e.g. Verizon.
  	 */
  	public static String getBrand() {
  		return android.os.Build.BRAND;
  	}
  
  	/**
  	 * Return misc system details that we might want to use later. Currently
  	 * does nothing.
  	 * 
  	 * @return
  	 */
  	public static Map<String, String> getSystemDetails() {
  		Map<String, String> results = new HashMap<String, String>();
  		// TODO: Some of this should be added to registration to identify the
  		// device and OS.
  		// Cyanogenmod and others may have different kernels etc that affect
  		// performance.
  
  		/*
  		 * Log.d("SetModel", "board:" + android.os.Build.BOARD);
  		 * Log.d("SetModel", "bootloader:" + android.os.Build.BOOTLOADER);
  		 * Log.d("SetModel", "brand:" + android.os.Build.BRAND);
  		 * Log.d("SetModel", "CPU_ABI 1 and 2:" + android.os.Build.CPU_ABI +
  		 * ", " + android.os.Build.CPU_ABI2); Log.d("SetModel", "dev:" +
  		 * android.os.Build.DEVICE); Log.d("SetModel", "disp:" +
  		 * android.os.Build.DISPLAY); Log.d("SetModel", "FP:" +
  		 * android.os.Build.FINGERPRINT); Log.d("SetModel", "HW:" +
  		 * android.os.Build.HARDWARE); Log.d("SetModel", "host:" +
  		 * android.os.Build.HOST); Log.d("SetModel", "ID:" +
  		 * android.os.Build.ID); Log.d("SetModel", "manufacturer:" +
  		 * android.os.Build.MANUFACTURER); Log.d("SetModel", "prod:" +
  		 * android.os.Build.PRODUCT); Log.d("SetModel", "radio:" +
  		 * android.os.Build.RADIO); // FIXME: SERIAL not available on 2.2 //
  		 * Log.d("SetModel", "ser:" + android.os.Build.SERIAL);
  		 * Log.d("SetModel", "tags:" + android.os.Build.TAGS); Log.d("SetModel",
  		 * "time:" + android.os.Build.TIME); Log.d("SetModel", "type:" +
  		 * android.os.Build.TYPE); Log.d("SetModel", "unknown:" +
  		 * android.os.Build.UNKNOWN); Log.d("SetModel", "user:" +
  		 * android.os.Build.USER); Log.d("SetModel", "model:" +
  		 * android.os.Build.MODEL); Log.d("SetModel", "codename:" +
  		 * android.os.Build.VERSION.CODENAME); Log.d("SetModel", "release:" +
  		 * android.os.Build.VERSION.RELEASE);
  		 */
  
  		return results;
  	}
  
  	/**
  	 * Read memory information from /proc/meminfo. Return used, free, inactive,
  	 * and active memory.
  	 * 
  	 * @return an int[] with used, free, inactive, and active memory, in kB, in
  	 *         that order.
  	 */
  	public static int[] readMeminfo() {
  		try {
  			RandomAccessFile reader = new RandomAccessFile("/proc/meminfo", "r");
  			String load = reader.readLine();
  
  			String[] toks = load.split("\\s+");
  			// Log.v("meminfo", "Load: " + load + " 1:" + toks[1]);
  			int total = Integer.parseInt(toks[1]);
  			load = reader.readLine();
  			toks = load.split("\\s+");
  			// Log.v("meminfo", "Load: " + load + " 1:" + toks[1]);
  			int free = Integer.parseInt(toks[1]);
  			load = reader.readLine();
  			load = reader.readLine();
  			load = reader.readLine();
  			load = reader.readLine();
  			toks = load.split("\\s+");
  			// Log.v("meminfo", "Load: " + load + " 1:" + toks[1]);
  			int act = Integer.parseInt(toks[1]);
  			load = reader.readLine();
  			toks = load.split("\\s+");
  			// Log.v("meminfo", "Load: " + load + " 1:" + toks[1]);
  			int inact = Integer.parseInt(toks[1]);
  			reader.close();
  			return new int[] { total - free, free, inact, act };
  		} catch (IOException ex) {
  			ex.printStackTrace();
  		}
  
  		return new int[] { 0, 0, 0, 0 };
  	}
  
  	/**
  	 * Read memory usage using the public Android API methods in
  	 * ActivityManager, such as MemoryInfo and getProcessMemoryInfo.
  	 * 
  	 * @param c
  	 *            the Context from the running Activity.
  	 * @return int[] with total and used memory, in kB, in that order.
  	 */
  	public static int[] readMemory(Context c) {
  		ActivityManager man = (ActivityManager) c.getSystemService(Activity.ACTIVITY_SERVICE);
  		/* Get available (free) memory */
  		ActivityManager.MemoryInfo info = new ActivityManager.MemoryInfo();
  		man.getMemoryInfo(info);
  		int totalMem = (int) info.availMem;
  
  		/* Get memory used by all running processes. */
  
  		/* Step 1: gather pids */
  		List<ActivityManager.RunningAppProcessInfo> procs = man.getRunningAppProcesses();
  		List<ActivityManager.RunningServiceInfo> servs = man.getRunningServices(Integer.MAX_VALUE);
  		int[] pids = new int[procs.size() + servs.size()];
  		int i = 0;
  		for (ActivityManager.RunningAppProcessInfo pinfo : procs) {
  			pids[i] = pinfo.pid;
  			i++;
  		}
  		for (ActivityManager.RunningServiceInfo pinfo : servs) {
  			pids[i] = pinfo.pid;
  			i++;
  		}
  
  		/*
  		 * Step 2: Sum up Pss values (weighted memory usage, taking into account
  		 * shared page usage)
  		 */
  		android.os.Debug.MemoryInfo[] mems = man.getProcessMemoryInfo(pids);
  		int memUsed = 0;
  		for (android.os.Debug.MemoryInfo mem : mems) {
  			memUsed += mem.getTotalPss();
  		}
  		Log.v("Mem", "Total mem:" + totalMem);
  		Log.v("Mem", "Mem Used:" + memUsed);
  		return new int[] { totalMem, memUsed };
  	}
  
  	// FIXME: Describe this. Why are there so many fields? Why is it divided by
  	// 100?
  	/*
  	 * The value of HZ varies across kernel versions and hardware platforms. On
  	 * i386 the situation is as follows: on kernels up to and including 2.4.x,
  	 * HZ was 100, giving a jiffy value of 0.01 seconds; starting with 2.6.0, HZ
  	 * was raised to 1000, giving a jiffy of 0.001 seconds. Since kernel 2.6.13,
  	 * the HZ value is a kernel configuration parameter and can be 100, 250 (the
  	 * default) or 1000, yielding a jiffies value of, respectively, 0.01, 0.004,
  	 * or 0.001 seconds. Since kernel 2.6.20, a further frequency is available:
  	 * 300, a number that divides evenly for the common video frame rates (PAL,
  	 * 25 HZ; NTSC, 30 HZ).
  	 * 
  	 * I will leave the unit of cpu time as the jiffy and we can discuss later.
  	 * 
  	 * 0 name of cpu 1 space 2 user time 3 nice time 4 sys time 5 idle time(it
  	 * is not include in the cpu total time) 6 iowait time 7 irg time 8 softirg
  	 * time
  	 * 
  	 * the idleTotal[5] is the idle time which always changes. There are two
  	 * spaces between cpu and user time.That is a tricky thing and messed up
  	 * splitting.:)
  	 */
  
  	/**
  	 * Read CPU usage from /proc/stat, return a fraction of
  	 * usage/(usage+idletime)
  	 * 
  	 * @return a fraction of usage/(usage+idletime)
  	 */
  	public static long[] readUsagePoint() {
  		try {
  			RandomAccessFile reader = new RandomAccessFile("/proc/stat", "r");
  			String load = reader.readLine();
  
  			String[] toks = load.split(" ");
  
  			long idle1 = Long.parseLong(toks[5]);
  			long cpu1 = Long.parseLong(toks[2]) + Long.parseLong(toks[3]) + Long.parseLong(toks[4])
  					+ Long.parseLong(toks[6]) + Long.parseLong(toks[7]) + Long.parseLong(toks[8]);
  
  			reader.close();
  			return new long[] { idle1, cpu1 };
  		} catch (IOException ex) {
  			ex.printStackTrace();
  		}
  
  		return null;
  	}
  
  	/**
  	 * Calculate CPU usage between the cpu and idle time given at two time
  	 * points.
  	 * 
  	 * @param then
  	 * @param now
  	 * @return
  	 */
  	public static double getUsage(long[] then, long[] now) {
  		if (then == null || now == null || then.length < 2 || now.length < 2)
  			return 0.0;
  		double idleAndCpuDiff = (now[0] + now[1]) - (then[0] + then[1]);
  		return (now[1] - then[1]) / idleAndCpuDiff;
  	}
  
  	private static WeakReference<List<RunningAppProcessInfo>> runningAppInfo = null;
  
  	public static List<ProcessInfo> getRunningAppInfo(Context c) {
  		List<RunningAppProcessInfo> runningProcs = getRunningProcessInfo(c);
  		List<RunningServiceInfo> runningServices = getRunningServiceInfo(c);
  
  		List<ProcessInfo> l = new ArrayList<ProcessInfo>();
  
  		if (runningProcs != null) {
  			for (RunningAppProcessInfo pi : runningProcs) {
  				if (pi == null)
  					continue;
  				ProcessInfo item = new ProcessInfo();
  				item.setImportance(CaratApplication.importanceString(pi.importance));
  				item.setPId(pi.pid);
  				item.setPName(pi.processName);
  				l.add(item);
  			}
  		}
  
  		if (runningServices != null) {
  			for (RunningServiceInfo pi : runningServices) {
  				if (pi == null)
  					continue;
  				ProcessInfo item = new ProcessInfo();
  				item.setImportance(pi.foreground ? "Foreground app" : "Service");
  				item.setPId(pi.pid);
  				item.setPName(pi.clientPackage);
  				l.add(item);
  			}
  		}
  
  		return l;
  	}
  
  	/**
  	 * Populate running process info into the runningAppInfo WeakReference list, and return its value.
  	 * @param context the Context
  	 * @return the value of the runningAppInfo WeakReference list after setting it.
  	 */
  	private static List<RunningAppProcessInfo> getRunningProcessInfo(Context context) {
  		if (runningAppInfo == null || runningAppInfo.get() == null) {
  			ActivityManager pActivityManager = (ActivityManager) context.getSystemService(Activity.ACTIVITY_SERVICE);
  			List<RunningAppProcessInfo> runningProcs = pActivityManager.getRunningAppProcesses();
  			/*
  			 * TODO: Is this the right thing to do? Remove part after ":" in
  			 * process names
  			 */
  			for (RunningAppProcessInfo i : runningProcs) {
  				if (i != null && i.processName != null) {
  					int idx = i.processName.lastIndexOf(':');
  					if (idx <= 0)
  						idx = i.processName.length();
  					i.processName = i.processName.substring(0, idx);
  				}
  			}
  
  			runningAppInfo = new WeakReference<List<RunningAppProcessInfo>>(runningProcs);
  		}
  		return runningAppInfo.get();
  	}
  
  	/**
  	 * Returns a list of currently running Services.
  	 * @param c the Context.
  	 * @return Returns a list of currently running Services.
  	 */
  	public static List<RunningServiceInfo> getRunningServiceInfo(Context c) {
  		ActivityManager pActivityManager = (ActivityManager) c.getSystemService(Activity.ACTIVITY_SERVICE);
  		return pActivityManager.getRunningServices(0);
  	}
  
  	/**
  	 * Helper to query whether an application is currently running and its code has not been evicted from memory. 
  	 * @param context the Context
  	 * @param appName the package name or process name of the application.
  	 * @return true if the application is running, false otherwise.
  	 */
  	public static boolean isRunning(Context context, String appName) {
  		List<RunningAppProcessInfo> runningProcs = getRunningProcessInfo(context);
  		for (RunningAppProcessInfo i : runningProcs) {
  			if (i.processName.equals(appName) && i.importance != RunningAppProcessInfo.IMPORTANCE_EMPTY)
  				return true;
  		}
  		return false;
  	}
  
  	public static boolean isSettingsSuggestion(Context context, String appName) {
  		// TODO: fill in (if everything is a suggestion, and no need for checking at the client side, then remove this method)
  		return true;
  	}
  	
  	/**
  	 * Used to clear the runningAppInfo WeakReference list when Carat is paused, so that it is refreshed when the process list view is shown.
  	 */
  	public static void resetRunningProcessInfo() {
  		runningAppInfo = null;
  	}
  
  	/**
  	 * package name to packageInfo map for quick querying.
  	 */
  	static WeakReference<Map<String, PackageInfo>> packages = null;
  
  	/**
  	 * Returns true if an application should be hidden in the UI. Uses the blacklist downloaded from Carat servers.
  	 * @param c the Context
  	 * @param processName the process name
  	 * @return true if the process should be hidden from the user, usually because it is an unkillable application that belongs to the system.
  	 */
  	public static boolean isHidden(Context c, String processName) {
  		boolean isSystem = isSystem(c, processName);
  		boolean blocked = isDisabled(c, processName) || (isSystem && !isWhiteListed(c, processName));
  		return blocked || isBlacklisted(c, processName);
  	}
  
  	/**
  	 * We currently do not employ a whitelist, so this returns true iff isBlacklisted(c, processName) returns false and vice versa.
  	 * 
  	 * @param c the Context.
  	 * @param processName the process name.
  	 * @return true iff isBlacklisted(c, processName) returns false and vice versa.
  	 */
  	private static boolean isWhiteListed(Context c, String processName) {
  		return !isBlacklisted(c, processName);
  	}
  
  	/**
  	 * Returns true if the processName matches an intem on the blacklist downloaded from Carat servers. 
  	 * 
  	 * @param c the Context.
  	 * @param processName the process name.
  	 * @return true if the processName matches an intem on the blacklist downloaded from Carat servers.
  	 */
  	private static boolean isBlacklisted(Context c, String processName) {
  		/*
  		 * Whitelist: Messaging, Voice Search, Bluetooth Share
  		 * 
  		 * Blacklist: Key chain, google partner set up, package installer,
  		 * package access helper
  		 */
  		if (CaratApplication.storage != null) {
  			List<String> blacklist = CaratApplication.storage.getBlacklist();
  			if (blacklist != null && blacklist.size() > 0 && processName != null && blacklist.contains(processName)) {
  				return true;
  			}
  
  			blacklist = CaratApplication.storage.getGloblist();
  			if (blacklist != null && blacklist.size() > 0 && processName != null) {
  				for (String glob : blacklist) {
  					if (glob == null)
  						continue;
  					// something*
  					if (glob.endsWith("*") && processName.startsWith(glob.substring(0, glob.length() - 1)))
  						return true;
  					// *something
  					if (glob.startsWith("*") && processName.endsWith(glob.substring(1)))
  						return true;
  				}
  			}
  		}
  		String label = CaratApplication.labelForApp(c, processName);
  
  		if (processName != null && label != null && label.equals(processName)) {
  			// Log.v("Hiding uninstalled", processName);
  			return true;
  		}
  
  		// FlurryAgent.logEvent("Whitelisted "+processName + " \""+ label+"\"");
  		return false;
  	}
  
  	/**
  	 * Returns true if the application is preinstalled on the device.
  	 * This usually means it is a system application, e.g. Key chain, google partner set up, package installer, package access helper.
  	 * We currently do not filter these out, because some of them are killable by the user, and not part of the core system, even if they are preinstalled on the device. 
  	 * @param context the Context.
  	 * @param processName the process name.
  	 * @return true if the application is preinstalled on the device.
  	 */
  	private static boolean isSystem(Context context, String processName) {
  		PackageInfo pak = getPackageInfo(context, processName);
  		if (pak != null) {
  			ApplicationInfo i = pak.applicationInfo;
  			int flags = i.flags;
  			boolean isSystemApp = (flags & ApplicationInfo.FLAG_SYSTEM) > 0;
  			isSystemApp = isSystemApp || (flags & ApplicationInfo.FLAG_UPDATED_SYSTEM_APP) > 0;
  			// Log.v(STAG, processName + " is System app? " + isSystemApp);
  			return isSystemApp;
  		}
  		return false;
  	}
  	
  	public static boolean isDisabled(Context c, String processName) {
  	    PackageManager pm = c.getPackageManager();
          if (pm == null)
              return false;
          try {
              ApplicationInfo info = pm.getApplicationInfo(processName, 0);
              boolean disabled = !info.enabled;
              /* If an app is disabled, schedule it for sending with the next sample.
               * This is triggered in the UI, so the amount of times that an app being
               * disabled is sent is limited to the number of times the user refreshes Carat
               * between two analysis runs. Disabled applications will then be recorded by
               * the analysis, and not sent to the client when they ask for hogs/bugs after that.
               * Over time, Carat then follows users' Hogs and Bugs better, knowing which apps are
               * disabled.
               */
              if (disabled) {
                  Log.i(STAG, "DISABLED: " + processName);
                  Editor e = PreferenceManager.getDefaultSharedPreferences(c.getApplicationContext()).edit();
                  e.putBoolean(SamplingLibrary.DISABLED + processName, true).commit();
              }
              return disabled;
          } catch (NameNotFoundException e) {
              Log.d(STAG, "Could not find app info for: "+processName);
          }
  	    return false;
  	}
  
  	/**
  	 * Helper to ensure the WeakReferenced `packages` is populated.
  	 * 
  	 * @param context
  	 * @return The content of `packages` or null in case of failure.
  	 */
  	private static Map<String, PackageInfo> getPackages(Context context) {
  		List<android.content.pm.PackageInfo> packagelist = null;
  
  		if (packages == null || packages.get() == null || packages.get().size() == 0) {
  			Map<String, PackageInfo> mp = new HashMap<String, PackageInfo>();
  			PackageManager pm = context.getPackageManager();
  			if (pm == null)
  				return null;
  
  			try {
  				if (collectSignatures)
  					packagelist = pm.getInstalledPackages(PackageManager.GET_SIGNATURES
  							| PackageManager.GET_PERMISSIONS);
  				else
  					packagelist = pm.getInstalledPackages(0);
  			} catch (Throwable th) {
  				// Forget about it...
  			}
  			if (packagelist == null)
  				return null;
  			for (PackageInfo pak : packagelist) {
  				if (pak == null || pak.applicationInfo == null || pak.applicationInfo.processName == null)
  					continue;
  				mp.put(pak.applicationInfo.processName, pak);
  			}
  
  			packages = new WeakReference<Map<String, PackageInfo>>(mp);
  
  			if (mp == null || mp.size() == 0)
  				return null;
  			return mp;
  		} else {
  			if (packages == null)
  				return null;
  			Map<String, PackageInfo> p = packages.get();
  			if (p == null || p.size() == 0)
  				return null;
  			return p;
  		}
  	}
  
  	/**
  	 * Get info for a single package from the WeakReferenced packagelist.
  	 * 
  	 * @param context
  	 * @param processName
  	 *            The package to get info for.
  	 * @return info for a single package from the WeakReferenced packagelist.
  	 */
  	public static PackageInfo getPackageInfo(Context context, String processName) {
  		Map<String, PackageInfo> mp = getPackages(context);
  		if (mp == null || !mp.containsKey(processName))
  			return null;
  		PackageInfo pak = mp.get(processName);
  		return pak;
  	}
  
  	/**
  	 * Returns a list of installed packages on the device. Will be called for
  	 * the first Carat sample on a phone, to get signatures for the malware
  	 * detection project. Later on, single package information is got by
  	 * receiving the package installed intent.
  	 * 
  	 * @param context
  	 * @param filterSystem
  	 *            if true, exclude system packages.
  	 * @return a list of installed packages on the device.
  	 */
  	public static Map<String, ProcessInfo> getInstalledPackages(Context context, boolean filterSystem) {
  		Map<String, PackageInfo> packageMap = getPackages(context);
  		PackageManager pm = context.getPackageManager();
  		if (pm == null)
  			return null;
  
  		Map<String, ProcessInfo> result = new HashMap<String, ProcessInfo>();
  
  		for (Entry<String, PackageInfo> pentry : packageMap.entrySet()) {
  			try {
  				String pkg = pentry.getKey();
  				PackageInfo pak = pentry.getValue();
  				if (pak != null) {
  					int vc = pak.versionCode;
  					ApplicationInfo appInfo = pak.applicationInfo;
  					String label = pm.getApplicationLabel(appInfo).toString();
  					// we need application UID to be able to use Android's
  					// TrafficStat API
  					// in order to get the traffic info of a particular app:
  					int appUid = appInfo.uid;
  					// get the amount of transmitted and received bytes by an
  					// app
  					// TODO: disabled for debugging
  //					TrafficRecord trafficRecord = getAppTraffic(appUid);
  
  					int flags = pak.applicationInfo.flags;
  					// Check if it is a system app
  					boolean isSystemApp = (flags & ApplicationInfo.FLAG_SYSTEM) > 0;
  					isSystemApp = isSystemApp || (flags & ApplicationInfo.FLAG_UPDATED_SYSTEM_APP) > 0;
  					if (filterSystem & isSystemApp)
  						continue;
  					if (pak.signatures.length > 0) {
  						List<String> sigList = getSignatures(pak);
  						ProcessInfo pi = new ProcessInfo();
  						pi.setPName(pkg);
  						pi.setApplicationLabel(label);
  						pi.setVersionCode(vc);
  						pi.setPId(-1);
  						pi.setIsSystemApp(isSystemApp);
  						pi.setAppSignatures(sigList);
  						pi.setImportance(Constants.IMPORTANCE_NOT_RUNNING);
  						pi.setInstallationPkg(pm.getInstallerPackageName(pkg));
  						pi.setVersionName(pak.versionName);
  						//TODO: disbaled for debugging
  //						pi.setTrafficRecord(trafficRecord);
  						result.put(pkg, pi);
  					}
  				}
  			} catch (Throwable th) {
  				// Forget about it...
  			}
  		}
  		return result;
  	}
  
  	/**
  	 * Returns info about an installed package. Will be called when receiving
  	 * the PACKAGE_ADDED or PACKAGE_REPLACED intent.
  	 * 
  	 * @param context
  	 * @param filterSystem
  	 *            if true, exclude system packages.
  	 * @return a list of installed packages on the device.
  	 */
  	public static ProcessInfo getInstalledPackage(Context context, String pkg) {
  		PackageManager pm = context.getPackageManager();
  		if (pm == null)
  			return null;
  		PackageInfo pak;
  		try {
  			pak = pm.getPackageInfo(pkg, PackageManager.GET_SIGNATURES | PackageManager.GET_PERMISSIONS);
  		} catch (NameNotFoundException e) {
  			return null;
  		}
  		if (pak == null)
  			return null;
  
  		ProcessInfo pi = new ProcessInfo();
  		int vc = pak.versionCode;
  		ApplicationInfo info = pak.applicationInfo;
  		String label = pm.getApplicationLabel(info).toString();
  		int flags = pak.applicationInfo.flags;
  		// Check if it is a system app
  		boolean isSystemApp = (flags & ApplicationInfo.FLAG_SYSTEM) > 0;
  		isSystemApp = isSystemApp || (flags & ApplicationInfo.FLAG_UPDATED_SYSTEM_APP) > 0;
  
  		if (pak.signatures.length > 0) {
  			List<String> sigList = getSignatures(pak);
  			pi.setPName(pkg);
  			pi.setApplicationLabel(label);
  			pi.setVersionCode(vc);
  			pi.setPId(-1);
  			pi.setIsSystemApp(isSystemApp);
  			pi.setAppSignatures(sigList);
  			pi.setImportance(Constants.IMPORTANCE_NOT_RUNNING);
  			pi.setInstallationPkg(pm.getInstallerPackageName(pkg));
  			pi.setVersionName(pak.versionName);
  		}
  		return pi;
  	}
  
  	/**
  	 * Returns a List of ProcessInfo objects, helper for getSample.
  	 * 
  	 * @param context the Context.
  	 * @return a List of ProcessInfo objects, helper for getSample.
  	 */
  	private static List<ProcessInfo> getRunningProcessInfoForSample(Context context) {
  		SharedPreferences p = PreferenceManager.getDefaultSharedPreferences(context);
  
  		// Reset list for each sample
  		runningAppInfo = null;
  		List<ProcessInfo> list = getRunningAppInfo(context);
  		List<ProcessInfo> result = new ArrayList<ProcessInfo>();
  
  		PackageManager pm = context.getPackageManager();
  		// Collected in the same loop to save computation.
  		int[] procMem = new int[list.size()];
  
  		Set<String> procs = new HashSet<String>();
  
  		boolean inst = p.getBoolean(Constants.PREFERENCE_SEND_INSTALLED_PACKAGES, true);
  
  		Map<String, ProcessInfo> ipkg = null;
  		if (inst)
  			ipkg = getInstalledPackages(context, false);
  
  		for (ProcessInfo pi : list) {
  			String pname = pi.getPName();
  			if (ipkg != null && ipkg.containsKey(pname))
  				ipkg.remove(pname);
  			procs.add(pname);
  			ProcessInfo item = new ProcessInfo();
  			PackageInfo pak = getPackageInfo(context, pname);
  			if (pak != null) {
  				String ver = pak.versionName;
  				int vc = pak.versionCode;
  				item.setVersionName(ver);
  				item.setVersionCode(vc);
  				ApplicationInfo info = pak.applicationInfo;
  
  				// Human readable label (if any)
  				String label = pm.getApplicationLabel(info).toString();
  				if (label != null && label.length() > 0)
  					item.setApplicationLabel(label);
  				int flags = pak.applicationInfo.flags;
  				// Check if it is a system app
  				boolean isSystemApp = (flags & ApplicationInfo.FLAG_SYSTEM) > 0;
  				isSystemApp = isSystemApp || (flags & ApplicationInfo.FLAG_UPDATED_SYSTEM_APP) > 0;
  				item.setIsSystemApp(isSystemApp);
  				/*
  				 * boolean sigSent = p.getBoolean(SIG_SENT_256 + pname, false);
  				 * if (collectSignatures && !sigSent && pak.signatures != null
  				 * && pak.signatures.length > 0) { List<String> sigList =
  				 * getSignatures(pak); boolean sigSentOld =
  				 * p.getBoolean(SIG_SENT + pname, false); if (sigSentOld)
  				 * p.edit().remove(SIG_SENT + pname);
  				 * p.edit().putBoolean(SIG_SENT_256 + pname, true).commit();
  				 * item.setAppSignatures(sigList); }
  				 */
  			}
  			item.setImportance(pi.getImportance());
  			item.setPId(pi.getPId());
  			item.setPName(pname);
  
  			String installationSource = null;
  			if (!pi.isSystemApp) {
  				try {
  					// Log.w(STAG, "Calling getInstallerPackageName with: " +
  					// pname);
  					installationSource = pm.getInstallerPackageName(pname);
  				} catch (IllegalArgumentException iae) {
  					Log.e(STAG, "Could not get installer for " + pname);
  				}
  			}
  			if (installationSource == null)
  				installationSource = "null";
  			item.setInstallationPkg(installationSource);
  
  			// procMem[list.indexOf(pi)] = pi.getPId();
  			// FIXME: More fields will need to be added here, but ProcessInfo
  			// needs to change.
  			/*
  			 * uid lru
  			 */
  			// add to result
  			result.add(item);
  		}
  
  		// Send installed packages if we were to do so.
  		if (ipkg != null && ipkg.size() > 0) {
  			result.addAll(ipkg.values());
  			p.edit().putBoolean(Constants.PREFERENCE_SEND_INSTALLED_PACKAGES, false).commit();
  		}
  
  		// Go through the preferences and look for UNINSTALL, INSTALL and
  		// REPLACE keys set by InstallReceiver.
  		Set<String> ap = p.getAll().keySet();
  		SharedPreferences.Editor e = p.edit();
  		boolean edited = false;
  		for (String pref : ap) {
  			if (pref.startsWith(INSTALLED)) {
  				String pname = pref.substring(INSTALLED.length());
  				boolean installed = p.getBoolean(pref, false);
  				if (installed) {
  					Log.i(STAG, "Installed:" + pname);
  					ProcessInfo i = getInstalledPackage(context, pname);
  					if (i != null) {
  						i.setImportance(Constants.IMPORTANCE_INSTALLED);
  						result.add(i);
  						e.remove(pref);
  						edited = true;
  					}
  				}
  			} else if (pref.startsWith(REPLACED)) {
  				String pname = pref.substring(REPLACED.length());
  				boolean replaced = p.getBoolean(pref, false);
  				if (replaced) {
  					Log.i(STAG, "Replaced:" + pname);
  					ProcessInfo i = getInstalledPackage(context, pname);
  					if (i != null) {
  						i.setImportance(Constants.IMPORTANCE_REPLACED);
  						result.add(i);
  						e.remove(pref);
  						edited = true;
  					}
  				}
  			} else if (pref.startsWith(UNINSTALLED)) {
  				String pname = pref.substring(UNINSTALLED.length());
  				boolean uninstalled = p.getBoolean(pref, false);
  				if (uninstalled) {
  					Log.i(STAG, "Uninstalled:" + pname);
  					result.add(uninstalledItem(pname, pref, e));
  					edited = true;
  				}
  			} else if (pref.startsWith(DISABLED)) {
                  String pname = pref.substring(DISABLED.length());
                  boolean disabled = p.getBoolean(pref, false);
                  if (disabled) {
                      Log.i(STAG, "Disabled app:" + pname);
                      result.add(disabledItem(pname, pref, e));
                      edited = true;
                  }
              }
  		}
  		if (edited)
  			e.commit();
  
  		// FIXME: These are not used yet.
  		/*
  		 * ActivityManager pActivityManager = (ActivityManager) context
  		 * .getSystemService(Activity.ACTIVITY_SERVICE); Debug.MemoryInfo[]
  		 * memoryInfo = pActivityManager .getProcessMemoryInfo(procMem); for
  		 * (Debug.MemoryInfo info : memoryInfo) { // Decide which ones of info.*
  		 * we want, add to a new and improved // ProcessInfo object // FIXME:
  		 * Not used yet, Sample needs more fields // FIXME: Which memory fields
  		 * to choose? //int memory = info.dalvikPrivateDirty; }
  		 */
  
  		return result;
  	}
  
  	/**
  	 * Helper to set application to the uninstalled state in the Carat sample.
  	 * @param pname the package that was uninstalled.
  	 * @param pref The preference that stored the uninstallation directive. This preference will be deleted to ensure uninstallations are not sent multiple times.
  	 * @param e the Editor (passed and not created here for efficiency)
  	 * @return a new ProcessInfo entry describing the uninstalled item.
  	 */
  	private static ProcessInfo uninstalledItem(String pname, String pref, SharedPreferences.Editor e) {
  		ProcessInfo item = new ProcessInfo();
  		item.setPName(pname);
  		List<String> sigs = new LinkedList<String>();
  		sigs.add("uninstalled");
  		item.setAppSignatures(sigs);
  		item.setPId(-1);
  		item.setImportance(Constants.IMPORTANCE_UNINSTALLED);
  		// Remember to remove it so we do not send
  		// multiple uninstall events
  		e.remove(pref);
  		return item;
  	}
  	
  	/**
       * Helper to set application to the disabled state in the Carat sample.
       * @param pname the package that was disabled.
       * @param pref The preference that stored the disabled directive. This preference will be deleted to ensure disabled apps are not sent multiple times.
       * @param e the Editor (passed and not created here for efficiency)
       * @return a new ProcessInfo entry describing the uninstalled item.
       */
      private static ProcessInfo disabledItem(String pname, String pref, SharedPreferences.Editor e) {
          ProcessInfo item = new ProcessInfo();
          item.setPName(pname);
          item.setPId(-1);
          item.setImportance(Constants.IMPORTANCE_DISABLED);
          // Remember to remove it so we do not send
          // multiple uninstall events
          e.remove(pref);
          return item;
      }
  
  	/**
  	 * Depratecated, use int[] meminfo = readMemInfo(); int totalMemory =
  	 * meminfo[0] + meminfo[1];
  	 */
  	@Deprecated
  	public static String getMemoryInfo() {
  		String tmp = null;
  		BufferedReader br = null;
  
  		try {
  			File file = new File("/proc/meminfo");
  			FileInputStream in = new FileInputStream(file);
  			br = new BufferedReader(new InputStreamReader(in), READ_BUFFER_SIZE);
  
  		} catch (FileNotFoundException e1) {
  			// TODO Auto-generated catch block
  			e1.printStackTrace();
  		}
  
  		try {
  			tmp = br.readLine();
  		} catch (IOException e) {
  			// TODO Auto-generated catch block
  			e.printStackTrace();
  		}
  
  		StringBuilder sMemory = new StringBuilder();
  		sMemory.append(tmp);
  
  		try {
  			tmp = br.readLine();
  			br.close();
  		} catch (IOException e) {
  			// TODO Auto-generated catch block
  			e.printStackTrace();
  		}
  
  		sMemory.append("\n").append(tmp).append("\n");
  		String result = "Memery Status:\n" + sMemory;
  		return result;
  
  	}
  
  	/*
  	 * Deprecated, use readMemInfo()[1]
  	 */
  	@Deprecated
  	public static String getMemoryFree() {
  		String tmp = null;
  		BufferedReader br = null;
  
  		try {
  			File file = new File("/proc/meminfo");
  			FileInputStream in = new FileInputStream(file);
  			br = new BufferedReader(new InputStreamReader(in), READ_BUFFER_SIZE);
  
  		} catch (FileNotFoundException e1) {
  			// TODO Auto-generated catch block
  			e1.printStackTrace();
  		}
  
  		try {
  			tmp = br.readLine();
  			tmp = br.readLine();
  			if (tmp != null) {
  				// split by whitespace and take 2nd element, so that in:
  				// MemoryFree: x kb
  				// the x remains.
  				String[] arr = tmp.split("\\s+");
  				if (arr.length > 1)
  					tmp = arr[1];
  			}
  			br.close();
  		} catch (IOException e) {
  			// TODO Auto-generated catch block
  			e.printStackTrace();
  		}
  		return tmp;
  	}
  
  	/**
  	 * Return time in seconds since last boot.
  	 */
  	public static double getUptime() {
  		long uptime = SystemClock.elapsedRealtime();
  		/*
  		 * int seconds = (int) (uptime / 1000) % 60; int minutes = (int) (uptime
  		 * / (1000 * 60) % 60); int hours = (int) (uptime / (1000 * 60 * 60) %
  		 * 24); String tmp = "\nThe uptime is :" + hours + "hr:" + minutes +
  		 * "mins:" + seconds + "sec.\n"; return tmp;
  		 */
  		Log.v("uptime", String.valueOf(uptime));
  		return uptime / 1000.0;
  	}
  
  	/**
  	 * Get the network status, one of connected, disconnected, connecting, or disconnecting.
  	 * @param context the Context.
  	 * @return the network status, one of connected, disconnected, connecting, or disconnecting.
  	 */
  	public static String getNetworkStatus(Context context) {
  		ConnectivityManager cm = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
  		if (cm == null)
  			return NETWORKSTATUS_DISCONNECTED;
  		NetworkInfo i = cm.getActiveNetworkInfo();
  		if (i == null)
  			return NETWORKSTATUS_DISCONNECTED;
  		NetworkInfo.State s = i.getState();
  		if (s == NetworkInfo.State.CONNECTED)
  			return NETWORKSTATUS_CONNECTED;
  		if (s == NetworkInfo.State.DISCONNECTED)
  			return NETWORKSTATUS_DISCONNECTED;
  		if (s == NetworkInfo.State.CONNECTING)
  			return NETWORKSTATUS_CONNECTING;
  		if (s == NetworkInfo.State.DISCONNECTING)
  			return NETWORKSTATUS_DISCONNECTING;
  		else
  			return NETWORKSTATUS_DISCONNECTED;
  	}
  
  	
  	/**
  	 * Get the network type, for example Wifi, mobile, wimax, or none.
  	 * 
  	 * @param context
  	 * @return
  	 */
  	public static String getNetworkType(Context context) {
  		ConnectivityManager cm = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
  		if (cm == null)
  			return TYPE_UNKNOWN;
  		NetworkInfo i = cm.getActiveNetworkInfo();
  		if (i == null)
  			return TYPE_UNKNOWN;
  		return i.getTypeName();
  	}
  
  	/**
  	 * Returns true if the Internet is reachable.
  	 * @param c the Context
  	 * @return true if the Internet is reachable.
  	 */
  	public static boolean networkAvailable(Context c) {
  		String network = getNetworkStatus(c);
  		return network.equals(NETWORKSTATUS_CONNECTED);
  	}
  
  	/* Get current WiFi signal Strength */
  	public static int getWifiSignalStrength(Context context) {
  		WifiManager myWifiManager = (WifiManager) context.getSystemService(Context.WIFI_SERVICE);
  		WifiInfo myWifiInfo = myWifiManager.getConnectionInfo();
  		int wifiRssi = myWifiInfo.getRssi();
  		// Log.v("WifiRssi", "Rssi:" + wifiRssi);
  		return wifiRssi;
  
  	}
  
  	/**
  	 * Get Wifi MAC ADDR. Hashed and used in UUID calculation.
  	 */
  	private static String getWifiMacAddress(Context context) {
  		WifiManager myWifiManager = (WifiManager) context.getSystemService(Context.WIFI_SERVICE);
  		if (myWifiManager == null)
  			return null;
  		WifiInfo myWifiInfo = myWifiManager.getConnectionInfo();
  		if (myWifiInfo == null)
  			return null;
  		return myWifiInfo.getMacAddress();
  	}
  
  	/* Get current WiFi link speed */
  	public static int getWifiLinkSpeed(Context context) {
  		WifiManager myWifiManager = (WifiManager) context.getSystemService(Context.WIFI_SERVICE);
  		WifiInfo myWifiInfo = myWifiManager.getConnectionInfo();
  		int linkSpeed = myWifiInfo.getLinkSpeed();
  
  		// Log.v("linkSpeed", "Link speed:" + linkSpeed);
  		return linkSpeed;
  	}
  
  	/* Check whether WiFi is enabled */
  	public static boolean getWifiEnabled(Context context) {
  		boolean wifiEnabled = false;
  
  		WifiManager myWifiManager = (WifiManager) context.getSystemService(Context.WIFI_SERVICE);
  		wifiEnabled = myWifiManager.isWifiEnabled();
  		// Log.v("WifiEnabled", "Wifi is enabled:" + wifiEnabled);
  		return wifiEnabled;
  	}
  
  	/* Get Wifi state: */
  	public static String getWifiState(Context context) {
  		WifiManager myWifiManager = (WifiManager) context.getSystemService(Context.WIFI_SERVICE);
  		int wifiState = myWifiManager.getWifiState();
  		switch (wifiState) {
  		case WifiManager.WIFI_STATE_DISABLED:
  			return WIFI_STATE_DISABLED;
  		case WifiManager.WIFI_STATE_DISABLING:
  			return WIFI_STATE_DISABLING;
  		case WifiManager.WIFI_STATE_ENABLED:
  			return WIFI_STATE_ENABLED;
  		case WifiManager.WIFI_STATE_ENABLING:
  			return WIFI_STATE_ENABLING;
  		default:
  			return WIFI_STATE_UNKNOWN;
  		}
  	}
  
  	public static WifiInfo getWifiInfo(Context context) {
  		WifiManager myWifiManager = (WifiManager) context.getSystemService(Context.WIFI_SERVICE);
  		WifiInfo connectionInfo = myWifiManager.getConnectionInfo();
  		// Log.v("WifiInfo", "Wifi information:" + connectionInfo);
  		return connectionInfo;
  
  	}
  
  	/*
  	 * This method is deprecated. As of ICE_CREAM_SANDWICH, availability of
  	 * background data depends on several combined factors, and this method will
  	 * always return true. Instead, when background data is unavailable,
  	 * getActiveNetworkInfo() will now appear disconnected.
  	 */
  	/* Check whether background data are enabled */
  	@Deprecated
  	public static boolean getBackgroundDataEnabled(Context context) {
  		boolean bacDataEnabled = false;
  		try {
  			if (Settings.Secure.getInt(context.getContentResolver(), Settings.Secure.BACKGROUND_DATA) == 1) {
  				bacDataEnabled = true;
  			}
  		} catch (SettingNotFoundException e) {
  			// TODO Auto-generated catch block
  			e.printStackTrace();
  		}
  		// Log.v("BackgroundDataEnabled", "Background data enabled? " +
  		// bacDataEnabled);
  		// return bacDataEnabled;
  		return true;
  	}
  
  	/* Get Current Screen Brightness Value */
  	public static int getScreenBrightness(Context context) {
  
  		int screenBrightnessValue = 0;
  		try {
  			screenBrightnessValue = android.provider.Settings.System.getInt(context.getContentResolver(),
  					android.provider.Settings.System.SCREEN_BRIGHTNESS);
  		} catch (SettingNotFoundException e) {
  			// TODO Auto-generated catch block
  			e.printStackTrace();
  		}
  
  		// Log.v("ScreenBrightness", "Screen brightness value:" +
  		// screenBrightnessValue);
  		return screenBrightnessValue;
  	}
  
  	public static boolean isAutoBrightness(Context context) {
  		boolean autoBrightness = false;
  		try {
  			autoBrightness = Settings.System.getInt(context.getContentResolver(),
  					Settings.System.SCREEN_BRIGHTNESS_MODE) == Settings.System.SCREEN_BRIGHTNESS_MODE_AUTOMATIC;
  		} catch (SettingNotFoundException e) {
  			e.printStackTrace();
  		}
  		// Log.v("AutoScreenBrightness",
  		// "Automatic Screen brightness mode is enabled:" + autoBrightness);
  		return autoBrightness;
  	}
  
  	/* Check whether GPS are enabled */
  	public static boolean getGpsEnabled(Context context) {
  		boolean gpsEnabled = false;
  		LocationManager myLocationManager = (LocationManager) context.getSystemService(Context.LOCATION_SERVICE);
  
  		gpsEnabled = myLocationManager.isProviderEnabled(LocationManager.GPS_PROVIDER);
  		// Log.v("GPS", "GPS is :" + gpsEnabled);
  		return gpsEnabled;
  	}
  
  	/* check the GSM cell information */
  	public static CellInfo getCellInfo(Context context) {
  		CellInfo curCell = new CellInfo();
  
  		TelephonyManager myTelManager = (TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE);
  
  		String netOperator = myTelManager.getNetworkOperator();
  
  		// Fix crash when not connected to network (airplane mode, underground,
  		// etc)
  		if (netOperator == null || netOperator.length() < 3) {
  			return curCell;
  		}
  
  		/*
  		 * FIXME: Actually check for mobile network status == connected before
  		 * doing this stuff.
  		 */
  
  		if (SamplingLibrary.getPhoneType(context) == PHONE_TYPE_CDMA) {
  			CdmaCellLocation cdmaLocation = (CdmaCellLocation) myTelManager.getCellLocation();
  			if (cdmaLocation == null) {
  				// Log.v("cdmaLocation", "CDMA Location:" + cdmaLocation);
  			} else {
  				int cid = cdmaLocation.getBaseStationId();
  				int lac = cdmaLocation.getNetworkId();
  				int mnc = cdmaLocation.getSystemId();
  				int mcc = Integer.parseInt(netOperator.substring(0, 3));
  
  				curCell.CID = cid;
  				curCell.LAC = lac;
  				curCell.MNC = mnc;
  				curCell.MCC = mcc;
  				curCell.radioType = SamplingLibrary.getMobileNetworkType(context);
  
  				// Log.v("MCC", "MCC is:" + mcc);
  				// Log.v("MNC", "MNC is:" + mnc);
  				// Log.v("CID", "CID is:" + cid);
  				// Log.v("LAC", "LAC is:" + lac);
  			}
  
  		} else if (SamplingLibrary.getPhoneType(context) == PHONE_TYPE_GSM) {
  			GsmCellLocation gsmLocation = (GsmCellLocation) myTelManager.getCellLocation();
  
  			if (gsmLocation == null) {
  				// Log.v("gsmLocation", "GSM Location:" + gsmLocation);
  			} else {
  				int cid = gsmLocation.getCid();
  				int lac = gsmLocation.getLac();
  				int mcc = Integer.parseInt(netOperator.substring(0, 3));
  				int mnc = Integer.parseInt(netOperator.substring(3));
  
  				curCell.MCC = mcc;
  				curCell.MNC = mnc;
  				curCell.LAC = lac;
  				curCell.CID = cid;
  				curCell.radioType = SamplingLibrary.getMobileNetworkType(context);
  
  				// Log.v("MCC", "MCC is:" + mcc);
  				// Log.v("MNC", "MNC is:" + mnc);
  				// Log.v("CID", "CID is:" + cid);
  				// Log.v("LAC", "LAC is:" + lac);
  			}
  		}
  		return curCell;
  	}
  
  	/**
  	 * Return distance between <code>lastKnownLocation</code> and a newly
  	 * obtained location from any available provider.
  	 * 
  	 * @param c
  	 *            from Intent or Application.
  	 * @return
  	 */
  	public static double getDistance(Context c) {
  		Location l = getLastKnownLocation(c);
  		double distance = 0.0;
  		if (lastKnownLocation != null && l != null) {
  			distance = lastKnownLocation.distanceTo(l);
  		}
  		lastKnownLocation = l;
  		return distance;
  	}
  
  	public static Location getLastKnownLocation(Context c) {
  		String provider = getBestProvider(c);
  		// FIXME: Some buggy device is giving GPS to us, even though we cannot
  		// use it.
  		if (provider != null && !provider.equals("gps")) {
  			Location l = getLastKnownLocation(c, provider);
  			return l;
  		}
  		return null;
  	}
  
  	private static Location getLastKnownLocation(Context context, String provider) {
  		LocationManager lm = (LocationManager) context.getSystemService(Context.LOCATION_SERVICE);
  		Location l = lm.getLastKnownLocation(provider);
  		return l;
  	}
  
  	/* Get the distance users between two locations */
  	public static double getDistance(double startLatitude, double startLongitude, double endLatitude,
  			double endLongitude) {
  		float[] results = new float[1];
  		Location.distanceBetween(startLatitude, startLongitude, endLatitude, endLongitude, results);
  		return results[0];
  	}
  
  	/**
  	 * Return a list of enabled LocationProviders, such as GPS, Network, etc.
  	 * 
  	 * @param context
  	 *            from onReceive or app.
  	 * @return
  	 */
  	public static List<String> getEnabledLocationProviders(Context context) {
  		LocationManager lm = (LocationManager) context.getSystemService(Context.LOCATION_SERVICE);
  		return lm.getProviders(true);
  	}
  
  	public static String getBestProvider(Context context) {
  		LocationManager lm = (LocationManager) context.getSystemService(Context.LOCATION_SERVICE);
  		Criteria c = new Criteria();
  		c.setAccuracy(Criteria.ACCURACY_COARSE);
  		c.setPowerRequirement(Criteria.POWER_LOW);
  		String provider = lm.getBestProvider(c, true);
  		return provider;
  	}
  
  	/* Check the maximum number of satellites can be used in the satellite list */
  	public static int getMaxNumSatellite(Context context) {
  
  		LocationManager locationManager = (LocationManager) context.getSystemService(Context.LOCATION_SERVICE);
  		GpsStatus gpsStatus = locationManager.getGpsStatus(null);
  		int maxNumSatellite = gpsStatus.getMaxSatellites();
  
  		// Log.v("maxNumStatellite", "Maxmium number of satellites:" +
  		// maxNumSatellite);
  		return maxNumSatellite;
  	}
  
  	/* Get call status */
  	public static String getCallState(Context context) {
  		TelephonyManager telManager = (TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE);
  
  		int callState = telManager.getCallState();
  		switch (callState) {
  		case TelephonyManager.CALL_STATE_OFFHOOK:
  			return CALL_STATE_OFFHOOK;
  		case TelephonyManager.CALL_STATE_RINGING:
  			return CALL_STATE_RINGING;
  		default:
  			return CALL_STATE_IDLE;
  		}
  	}
  
  	private static String getDeviceId(Context context) {
  		TelephonyManager telManager = (TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE);
  		if (telManager == null)
  			return null;
  		return telManager.getDeviceId();
  	}
  
  	/* Get network type */
  	public static String getMobileNetworkType(Context context) {
  		TelephonyManager telManager = (TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE);
  
  		int netType = telManager.getNetworkType();
  		switch (netType) {
  		case TelephonyManager.NETWORK_TYPE_1xRTT:
  			return NETWORK_TYPE_1xRTT;
  		case TelephonyManager.NETWORK_TYPE_CDMA:
  			return NETWORK_TYPE_CDMA;
  		case TelephonyManager.NETWORK_TYPE_EDGE:
  			return NETWORK_TYPE_EDGE;
  		case EHRPD:
  			return NETWORK_TYPE_EHRPD;
  		case TelephonyManager.NETWORK_TYPE_EVDO_0:
  			return NETWORK_TYPE_EVDO_0;
  		case TelephonyManager.NETWORK_TYPE_EVDO_A:
  			return NETWORK_TYPE_EVDO_A;
  		case EVDO_B:
  			return NETWORK_TYPE_EVDO_B;
  		case TelephonyManager.NETWORK_TYPE_GPRS:
  			return NETWORK_TYPE_GPRS;
  		case TelephonyManager.NETWORK_TYPE_HSDPA:
  			return NETWORK_TYPE_HSDPA;
  		case TelephonyManager.NETWORK_TYPE_HSPA:
  			return NETWORK_TYPE_HSPA;
  		case HSPAP:
  			return NETWORK_TYPE_HSPAP;
  		case TelephonyManager.NETWORK_TYPE_HSUPA:
  			return NETWORK_TYPE_HSUPA;
  		case TelephonyManager.NETWORK_TYPE_IDEN:
  			return NETWORK_TYPE_IDEN;
  		case LTE:
  			return NETWORK_TYPE_LTE;
  		case TelephonyManager.NETWORK_TYPE_UMTS:
  			return NETWORK_TYPE_UMTS;
  		default:
  			// If we don't know the type, just return the number and let the
  			// backend take care of it
  			return netType + "";
  		}
  	}
  
  	/* Get Phone Type */
  	public static String getPhoneType(Context context) {
  		TelephonyManager telManager = (TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE);
  
  		int phoneType = telManager.getPhoneType();
  		switch (phoneType) {
  		case TelephonyManager.PHONE_TYPE_CDMA:
  			return PHONE_TYPE_CDMA;
  		case TelephonyManager.PHONE_TYPE_GSM:
  			return PHONE_TYPE_GSM;
  		default:
  			return PHONE_TYPE_NONE;
  		}
  	}
  
  	/* Check is it network roaming */
  	public static boolean getRoamingStatus(Context context) {
  		boolean roamStatus = false;
  
  		TelephonyManager telManager = (TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE);
  
  		roamStatus = telManager.isNetworkRoaming();
  		// Log.v("RoamingStatus", "Roaming status:" + roamStatus);
  		return roamStatus;
  	}
  
  	/* Get data state */
  	public static String getDataState(Context context) {
  		TelephonyManager telManager = (TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE);
  
  		int dataState = telManager.getDataState();
  		switch (dataState) {
  		case TelephonyManager.DATA_CONNECTED:
  			return DATA_CONNECTED;
  		case TelephonyManager.DATA_CONNECTING:
  			return DATA_CONNECTING;
  		case TelephonyManager.DATA_DISCONNECTED:
  			return DATA_DISCONNECTED;
  		default:
  			return DATA_SUSPENDED;
  		}
  	}
  
  	/* Get data activity */
  	public static String getDataActivity(Context context) {
  		TelephonyManager telManager = (TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE);
  
  		int dataActivity = telManager.getDataActivity();
  		switch (dataActivity) {
  		case TelephonyManager.DATA_ACTIVITY_IN:
  			return DATA_ACTIVITY_IN;
  		case TelephonyManager.DATA_ACTIVITY_OUT:
  			return DATA_ACTIVITY_OUT;
  		case TelephonyManager.DATA_ACTIVITY_INOUT:
  			return DATA_ACTIVITY_INOUT;
  		default:
  			return DATA_ACTIVITY_NONE;
  		}
  	}
  
  	/* Get the current location of the device */
  	public static CellLocation getDeviceLocation(Context context) {
  		TelephonyManager telManager = (TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE);
  
  		CellLocation LocDevice = telManager.getCellLocation();
  		// Log.v("DeviceLocation", "Device Location:" + LocDevice);
  		return LocDevice;
  	}
  
  	/**
  	 * Return a long[3] with incoming call time, outgoing call time, and
  	 * non-call time in seconds since boot.
  	 * 
  	 * @param context
  	 *            from onReceive or Activity
  	 * @return a long[3] with incoming call time, outgoing call time, and
  	 *         non-call time in seconds since boot.
  	 */
  	public static long[] getCalltimesSinceBoot(Context context) {
  
  		long[] result = new long[3];
  
  		long callInSeconds = 0;
  		long callOutSeconds = 0;
  		int type;
  		long dur;
  
  		// ms since boot
  		long uptime = SystemClock.elapsedRealtime();
  		long now = System.currentTimeMillis();
  		long bootTime = now - uptime;
  
  		String[] queries = new String[] { android.provider.CallLog.Calls.TYPE, android.provider.CallLog.Calls.DATE,
  				android.provider.CallLog.Calls.DURATION };
  
  		Cursor cur = context.getContentResolver().query(android.provider.CallLog.Calls.CONTENT_URI, queries,
  				android.provider.CallLog.Calls.DATE + " > " + bootTime, null,
  				android.provider.CallLog.Calls.DATE + " ASC");
  
  		if (cur != null) {
  			if (cur.moveToFirst()) {
  				while (!cur.isAfterLast()) {
  					type = cur.getInt(0);
  					dur = cur.getLong(2);
  					switch (type) {
  					case android.provider.CallLog.Calls.INCOMING_TYPE:
  						callInSeconds += dur;
  						break;
  					case android.provider.CallLog.Calls.OUTGOING_TYPE:
  						callOutSeconds += dur;
  						break;
  					default:
  					}
  					cur.moveToNext();
  				}
  			} else {
  				Log.w("CallDurFromBoot", "No calls listed");
  			}
  			cur.close();
  		} else {
  			Log.w("CallDurFromBoot", "Cursor is null");
  		}
  
  		// uptime is ms, so it needs to be divided by 1000
  		long nonCallTime = uptime / 1000 - callInSeconds - callOutSeconds;
  		result[0] = callInSeconds;
  		result[1] = callOutSeconds;
  		result[2] = nonCallTime;
  		return result;
  	}
  
  	/* Get a monthly call duration record */
  	public static Map<String, CallMonth> getMonthCallDur(Context context) {
  
  		Map<String, CallMonth> callMonth = new HashMap<String, CallMonth>();
  		Map<String, String> callInDur = new HashMap<String, String>();
  		Map<String, String> callOutDur = new HashMap<String, String>();
  
  		int callType;
  		long callDur;
  		Date callDate;
  		String tmpTime = null;
  		String time;
  		SimpleDateFormat dateformat = new SimpleDateFormat("yyyy-MM");
  		CallMonth curMonth = null;
  
  		String[] queryFields = new String[] { android.provider.CallLog.Calls.TYPE, android.provider.CallLog.Calls.DATE,
  				android.provider.CallLog.Calls.DURATION };
  
  		Cursor myCursor = context.getContentResolver().query(android.provider.CallLog.Calls.CONTENT_URI, queryFields,
  				null, null, android.provider.CallLog.Calls.DATE + " DESC");
  
  		if (myCursor.moveToFirst()) {
  			for (int i = 0; i < myCursor.getColumnCount(); i++) {
  				myCursor.moveToPosition(i);
  				callType = myCursor.getInt(0);
  				callDate = new Date(myCursor.getLong(1));
  				callDur = myCursor.getLong(2);
  
  				time = dateformat.format(callDate);
  				if (tmpTime != null && !time.equals(tmpTime)) {
  					callMonth.put(tmpTime, curMonth);
  					callInDur.clear();
  					callOutDur.clear();
  					curMonth = new CallMonth();
  				}
  				tmpTime = time;
  
  				if (callType == 1) {
  					curMonth.tolCallInNum++;
  					curMonth.tolCallInDur += callDur;
  					callInDur.put("tolCallInNum", String.valueOf(curMonth.tolCallInNum));
  					callInDur.put("tolCallInDur", String.valueOf(curMonth.tolCallInDur));
  				}
  				if (callType == 2) {
  					curMonth.tolCallOutNum++;
  					curMonth.tolCallOutDur += callDur;
  					callOutDur.put("tolCallOutNum", String.valueOf(curMonth.tolCallOutNum));
  					callOutDur.put("tolCallOutDur", String.valueOf(curMonth.tolCallOutDur));
  				}
  				if (callType == 3) {
  					curMonth.tolMissedCallNum++;
  					callInDur.put("tolMissedCallNum", String.valueOf(curMonth.tolMissedCallNum));
  				}
  			}
  		} else {
  			// Log.v("MonthType", "callType=None");
  			// Log.v("MonthDate", "callDate=None");
  			// Log.v("MonthDuration", "callduration =None");
  		}
  		return callMonth;
  	}
  
  	public static CallMonth getCallMonthinfo(Context context, String time) {
  
  		Map<String, CallMonth> callInfo;
  		callInfo = SamplingLibrary.getMonthCallDur(context);
  		CallMonth call = new CallMonth();
  		call = callInfo.get(time);
  		return call;
  	}
  
  	private static Location lastKnownLocation = null;
  
  	/**
  	 * Get whether the screen is on or off.
  	 * 
  	 * @return true if the screen is on.
  	 */
  	public static int isScreenOn(Context context) {
  		android.os.PowerManager powerManager = (android.os.PowerManager) context
  				.getSystemService(Context.POWER_SERVICE);
  		if (powerManager != null)
  			if (powerManager.isScreenOn())
  				return 1;
  		return 0;
  	}
  
  	/**
  	 * Get the current timezone of the device.
  	 */
  
  	public static String getTimeZone(Context context) {
  		Calendar cal = Calendar.getInstance();
  		TimeZone tz = cal.getTimeZone();
  		return tz.getDisplayName();
  	}
  
  	/**
  	 * 
  	 * @param context
  	 * @return true when app installation from unknown sources is enabled.
  	 */
  	public static int allowUnknownSources(Context context) {
  		ContentResolver res = context.getContentResolver();
  		int unknownSources = Settings.Secure.getInt(res, Settings.Secure.INSTALL_NON_MARKET_APPS, 0);
  		return unknownSources;
  	}
  
  	/**
  	 * 
  	 * @param context
  	 * @return true when developer mode is enabled.
  	 */
  	public static int isDeveloperModeOn(Context context) {
  		ContentResolver res = context.getContentResolver();
  		int adb = Settings.Secure.getInt(res, Settings.Secure.ADB_ENABLED, 0);
  		// In API level 17, this is Settings.Global.ADB_ENABLED.
  		return adb;
  	}
  
  	/*
  	 * TODO: Make the app running when the system reboots, and provide a stop
  	 * button. CPU and Memory info per application CPU core/ frequency, CPU
  	 * governors usage How to motivate user to upload more samples.
  	 */
  
  	/**
  	 * Safely terminate (kill) the given app.
  	 * 
  	 * @param context
  	 * @param packageName
  	 * @param label
  	 * @return
  	 */
  	public static boolean killApp(Context context, String packageName, String label) {
  		ActivityManager am = (ActivityManager) context.getSystemService(Activity.ACTIVITY_SERVICE);
  		if (am != null) {
  			try {
  				PackageInfo p = getPackageInfo(context, packageName);
  				// Log.v(STAG, "Trying to kill proc=" + packageName + " pak=" +
  				// p.packageName);
  				FlurryAgent.logEvent("Killing app=" + (label == null ? "null" : label) + " proc=" + packageName
  						+ " pak=" + (p == null ? "null" : p.packageName));
  				am.killBackgroundProcesses(packageName);
  
  				return true;
  			} catch (Throwable th) {
  				Log.e(STAG, "Could not kill process: " + packageName, th);
  			}
  		}
  		return false;
  	}
  
  	private static String convertToHex(byte[] data) {
  		StringBuilder buf = new StringBuilder();
  		for (byte b : data) {
  			int halfbyte = (b >>> 4) & 0x0F;
  			int two_halfs = 0;
  			do {
  				buf.append((0 <= halfbyte) && (halfbyte <= 9) ? (char) ('0' + halfbyte)
  						: (char) ('a' + (halfbyte - 10)));
  				halfbyte = b & 0x0F;
  			} while (two_halfs++ < 1);
  		}
  		return buf.toString();
  	}
  
  	public static Sample getSample(Context context, Intent intent, String lastBatteryState) {
  		final String TAG = "SamplingLibrary.getSample";
  		Log.d(TAG, "getSample() was invoked.");
  
  		String action = intent.getAction();
  
  		Log.d(TAG, "action = " + action);
  
  		// Construct sample and return it in the end
  		Sample mySample = new Sample();
  		SharedPreferences p = PreferenceManager.getDefaultSharedPreferences(context);
  		String uuId = p.getString(CaratApplication.getRegisteredUuid(), null);
  		mySample.setUuId(uuId);
  		mySample.setTriggeredBy(action);
  		// required always
  		long now = System.currentTimeMillis();
  		mySample.setTimestamp(now / 1000.0);
  
  		// Record first data point for CPU usage
  		long[] idleAndCpu1 = readUsagePoint();
  
  		// If the sampler is running because of the SCREEN_ON or SCREEN_OFF
  		// event/action,
  		// we want to get the info of all installed apps/packages, not only
  		// those running.
  		// This is because we need the traffic info of all apps, some might not
  		// be running when
  		// those events (screen on / screen off) occur
  
  		// TODO: let's comment out these lines for debugging purpose
  
  		// if (action.equals(Intent.ACTION_SCREEN_ON) ||
  		// action.equals(Intent.ACTION_SCREEN_OFF)) {
  		// Log.d(TAG,
  		// "the action has been Intent.ACTION_SCREEN_ON or SCREEN_OFF. Taking sample of ALL INSTALLED packages (rather than running processes)");
  		// Map<String, ProcessInfo> installedPackages =
  		// getInstalledPackages(context, false);
  		// List<ProcessInfo> processes = new ArrayList<ProcessInfo>();
  		// processes.addAll(installedPackages.values());
  		// } else {
  		// Log.d(TAG,
  		// "the action has NOT been Intent.ACTION_SCREEN_ON or SCREEN_OFF. Taking sample of running processes.");
  		List<ProcessInfo> processes = getRunningProcessInfoForSample(context);
  		mySample.setPiList(processes);
  		// }
  
  		int screenBrightness = SamplingLibrary.getScreenBrightness(context);
  		mySample.setScreenBrightness(screenBrightness);
  		boolean autoScreenBrightness = SamplingLibrary.isAutoBrightness(context);
  		if (autoScreenBrightness)
  			mySample.setScreenBrightness(-1); // Auto
  		// boolean gpsEnabled = SamplingLibrary.getGpsEnabled(context);
  		// Location providers
  		List<String> enabledLocationProviders = SamplingLibrary.getEnabledLocationProviders(context);
  		mySample.setLocationProviders(enabledLocationProviders);
  
  		// TODO: not in Sample yet
  		// int maxNumSatellite = SamplingLibrary.getMaxNumSatellite(context);
  
  		String network = SamplingLibrary.getNetworkStatus(context);
  		String networkType = SamplingLibrary.getNetworkType(context);
  		String mobileNetworkType = SamplingLibrary.getMobileNetworkType(context);
  
  		// Required in new Carat protocol
  		if (network.equals(NETWORKSTATUS_CONNECTED)) {
  			if (networkType.equals("WIFI"))
  				mySample.setNetworkStatus(networkType);
  			else
  				mySample.setNetworkStatus(mobileNetworkType);
  		} else
  			mySample.setNetworkStatus(network);
  
  		// String ns = mySample.getNetworkStatus();
  		// Log.d(STAG, "Set networkStatus="+ns);
  
  		// Network details
  		NetworkDetails nd = new NetworkDetails();
  
  		// Network type
  		nd.setNetworkType(networkType);
  		nd.setMobileNetworkType(mobileNetworkType);
  		boolean roamStatus = SamplingLibrary.getRoamingStatus(context);
  		nd.setRoamingEnabled(roamStatus);
  		String dataState = SamplingLibrary.getDataState(context);
  		nd.setMobileDataStatus(dataState);
  		String dataActivity = SamplingLibrary.getDataActivity(context);
  		nd.setMobileDataActivity(dataActivity);
  
  		// Wifi stuff
  		String wifiState = SamplingLibrary.getWifiState(context);
  		nd.setWifiStatus(wifiState);
  		int wifiSignalStrength = SamplingLibrary.getWifiSignalStrength(context);
  		nd.setWifiSignalStrength(wifiSignalStrength);
  		int wifiLinkSpeed = SamplingLibrary.getWifiLinkSpeed(context);
  		nd.setWifiLinkSpeed(wifiLinkSpeed);
  		// Add NetworkDetails substruct to Sample
  		mySample.setNetworkDetails(nd);
  
  		/* Calling Information */
  		// List<String> callInfo;
  		// callInfo=SamplingLibrary.getCallInfo(context);
  		/* Total call time */
  		// long totalCallTime=0;
  		// totalCallTime=SamplingLibrary.getTotalCallDur(context);
  
  		/*
  		 * long[] incomingOutgoingIdle = getCalltimesSinceBoot(context);
  		 * Log.d(STAG, "Call time since boot: Incoming=" +
  		 * incomingOutgoingIdle[0] + " Outgoing=" + incomingOutgoingIdle[1] +
  		 * " idle=" + incomingOutgoingIdle[2]);
  		 * 
  		 * // Summary Call info CallInfo ci = new CallInfo(); String callState =
  		 * SamplingLibrary.getCallState(context); ci.setCallStatus(callState);
  		 * ci.setIncomingCallTime(incomingOutgoingIdle[0]);
  		 * ci.setOutgoingCallTime(incomingOutgoingIdle[1]);
  		 * ci.setNonCallTime(incomingOutgoingIdle[2]);
  		 * 
  		 * mySample.setCallInfo(ci);
  		 */
  
  		// Bundle b = intent.getExtras();
  
  		int health = intent.getIntExtra(BatteryManager.EXTRA_HEALTH, 0);
  		int status = intent.getIntExtra(BatteryManager.EXTRA_STATUS, 0);
  		// This is really an int.
  		// FIXED: Not used yet, Sample needs more fields
  
  		int plugged = intent.getIntExtra(BatteryManager.EXTRA_PLUGGED, 0);
  		String batteryTechnology = intent.getExtras().getString(BatteryManager.EXTRA_TECHNOLOGY);
  
  		// FIXED: Not used yet, Sample needs more fields
  		String batteryHealth = "Unknown";
  		String batteryStatus = "Unknown";
  
  		switch (health) {
  
  		case BatteryManager.BATTERY_HEALTH_DEAD:
  			batteryHealth = "Dead";
  			break;
  		case BatteryManager.BATTERY_HEALTH_GOOD:
  			batteryHealth = "Good";
  			break;
  		case BatteryManager.BATTERY_HEALTH_OVER_VOLTAGE:
  			batteryHealth = "Over voltage";
  			break;
  		case BatteryManager.BATTERY_HEALTH_OVERHEAT:
  			batteryHealth = "Overheat";
  			break;
  		case BatteryManager.BATTERY_HEALTH_UNKNOWN:
  			batteryHealth = "Unknown";
  			break;
  		case BatteryManager.BATTERY_HEALTH_UNSPECIFIED_FAILURE:
  			batteryHealth = "Unspecified failure";
  			break;
  		}
  
  		switch (status) {
  
  		case BatteryManager.BATTERY_STATUS_CHARGING:
  			batteryStatus = "Charging";
  			break;
  		case BatteryManager.BATTERY_STATUS_DISCHARGING:
  			batteryStatus = "Discharging";
  			break;
  		case BatteryManager.BATTERY_STATUS_FULL:
  			batteryStatus = "Full";
  			break;
  		case BatteryManager.BATTERY_STATUS_NOT_CHARGING:
  			batteryStatus = "Not charging";
  			break;
  		case BatteryManager.BATTERY_STATUS_UNKNOWN:
  			batteryStatus = "Unknown";
  			break;
  		default:
  			batteryStatus = lastBatteryState != null ? lastBatteryState : "Unknown";
  		}
  
  		// FIXED: Not used yet, Sample needs more fields
  		String batteryCharger = "unplugged";
  		switch (plugged) {
  
  		case BatteryManager.BATTERY_PLUGGED_AC:
  			batteryCharger = "ac";
  			break;
  		case BatteryManager.BATTERY_PLUGGED_USB:
  			batteryCharger = "usb";
  			break;
  		}
  
  		BatteryDetails bd = new BatteryDetails();
  		// otherInfo.setCPUIdleTime(totalIdleTime);
  
  		// IMPORTANT: All of the battery details fields were never set (=always
  		// zero), like the last battery level.
  		// Now all must have been fixed.
  
  		// current battery temperature in degrees Centigrade (the unit of the
  		// temperature value
  		// (returned by BatteryManager) is not Centigrade, it should be divided
  		// by 10)
  		int temperature = intent.getIntExtra(BatteryManager.EXTRA_TEMPERATURE, 0) / 10;
  		bd.setBatteryTemperature(temperature);
  		// otherInfo.setBatteryTemperature(temperature);
  
  		// current battery voltage in VOLTS (the unit of the returned value by
  		// BatteryManager is millivolts)
  		double voltage = intent.getIntExtra(BatteryManager.EXTRA_VOLTAGE, 0) / 1000;
  		bd.setBatteryVoltage(voltage);
  
  		// otherInfo.setBatteryVoltage(voltage);
  		bd.setBatteryTechnology(batteryTechnology);
  
  		bd.setBatteryCharger(batteryCharger);
  		bd.setBatteryHealth(batteryHealth);
  
  		mySample.setBatteryDetails(bd);
  
  		mySample.setBatteryLevel(currentBatteryLevel);
  		mySample.setBatteryState(batteryStatus);
  
  		int[] usedFreeActiveInactive = SamplingLibrary.readMeminfo();
  		if (usedFreeActiveInactive != null && usedFreeActiveInactive.length == 4) {
  			mySample.setMemoryUser(usedFreeActiveInactive[0]);
  			mySample.setMemoryFree(usedFreeActiveInactive[1]);
  			mySample.setMemoryActive(usedFreeActiveInactive[2]);
  			mySample.setMemoryInactive(usedFreeActiveInactive[3]);
  		}
  		// TODO: Memory Wired should have memory that is "unevictable", that
  		// will always be used even when all apps are killed
  
  		// Log.d(STAG, "serial=" + getBuildSerial());
  
  		// Record second data point for cpu/idle time
  		now = System.currentTimeMillis();
  		long[] idleAndCpu2 = readUsagePoint();
  
  		CpuStatus cs = new CpuStatus();
  
  		cs.setCpuUsage(getUsage(idleAndCpu1, idleAndCpu2));
  		cs.setUptime(getUptime());
  		mySample.setCpuStatus(cs);
  
  		mySample.setDeveloperMode(isDeveloperModeOn(context));
  		mySample.setUnknownSources(allowUnknownSources(context));
  		mySample.setScreenOn(isScreenOn(context));
  		mySample.setTimeZone(getTimeZone(context));
  		// printAverageFeaturePower(context);
  
  		// If there are extra fields, include them into the sample.
  		List<Feature> extras = getExtras(context);
  		if (extras != null && extras.size() > 0)
  			mySample.setExtra(extras);
  		
  		return mySample;
  	}
  
  	/**
  	 * Helper method to collect all the extra information we wish to add to the sample into the Extra Feature list.
  	 * @param context the Context
  	 * @return a List<Feature> populated with extra items to collect outside of the protocol spec.
  	 */
  	private static List<Feature> getExtras(Context context) {
  		LinkedList<Feature> res = new LinkedList<Feature>();
  		res.add(getVmVersion(context));
  		return res;
  	}
  
  	//TODO: disabled for debugging
  //	private static TrafficRecord getAppTraffic(Integer uid) {
  //		TrafficRecord trafficRecord = new TrafficRecord();
  //		trafficRecord.setTx(TrafficStats.getUidTxBytes(uid));
  //		trafficRecord.setRx(TrafficStats.getUidRxBytes(uid));
  //		return trafficRecord;
  //	}
  
  	/**
  	 * Get the java.vm.version system property as a Feature("vm", version).
  	 * @param context the Context.
  	 * @return a Feature instance with the key "vm" and value of the "java.vm.version" system property. 
  	 */
  	private static Feature getVmVersion(Context context) {
  		String vm = System.getProperty("java.vm.version");
  		if (vm == null)
  			vm = "";
  		Feature vmVersion = new Feature();
  		vmVersion.setKey("vm");
  		vmVersion.setValue(vm);
  		return vmVersion;
  	}
  
  	public static List<String> getSignatures(PackageInfo pak) {
  		List<String> sigList = new LinkedList<String>();
  		String[] pmInfos = pak.requestedPermissions;
  		if (pmInfos != null) {
  			byte[] bytes = getPermissionBytes(pmInfos);
  			String hexB = convertToHex(bytes);
  			sigList.add(hexB);
  		}
  		Signature[] sigs = pak.signatures;
  
  		for (Signature s : sigs) {
  			MessageDigest md = null;
  			try {
  				md = MessageDigest.getInstance("SHA-1");
  				md.update(s.toByteArray());
  				byte[] dig = md.digest();
  				// Add SHA-1
  				sigList.add(convertToHex(dig));
  
  				CertificateFactory fac = CertificateFactory.getInstance("X.509");
  				if (fac == null)
  					continue;
  				X509Certificate cert = (X509Certificate) fac.generateCertificate(new ByteArrayInputStream(s
  						.toByteArray()));
  				if (cert == null)
  					continue;
  				PublicKey pkPublic = cert.getPublicKey();
  				if (pkPublic == null)
  					continue;
  				String al = pkPublic.getAlgorithm();
  				if (al.equals("RSA")) {
  					md = MessageDigest.getInstance("SHA-256");
  					RSAPublicKey rsa = (RSAPublicKey) pkPublic;
  					byte[] data = rsa.getModulus().toByteArray();
  					if (data[0] == 0) {
  						byte[] copy = new byte[data.length - 1];
  						System.arraycopy(data, 1, copy, 0, data.length - 1);
  						md.update(copy);
  					} else
  						md.update(data);
  					dig = md.digest();
  					// Add SHA-256 of modulus
  					sigList.add(convertToHex(dig));
  				} else if (al.equals("DSA")) {
  					DSAPublicKey dsa = (DSAPublicKey) pkPublic;
  					md = MessageDigest.getInstance("SHA-256");
  					byte[] data = dsa.getY().toByteArray();
  					if (data[0] == 0) {
  						byte[] copy = new byte[data.length - 1];
  						System.arraycopy(data, 1, copy, 0, data.length - 1);
  						md.update(copy);
  					} else
  						md.update(data);
  					dig = md.digest();
  					// Add SHA-256 of public key (DSA)
  					sigList.add(convertToHex(dig));
  				} else {
  					Log.e("SamplingLibrary", "Weird algorithm: " + al + " for " + pak.packageName);
  				}
  			} catch (NoSuchAlgorithmException e) {
  				// Do nothing
  			} catch (CertificateException e) {
  				// Do nothing
  			}
  
  		}
  		return sigList;
  	}
  
  	public static byte[] getPermissionBytes(String[] perms) {
  		if (perms == null)
  			return null;
  		if (permList.size() == 0)
  			populatePermList();
  		// Log.i(STAG, "PermList Size: " + permList.size());
  		byte[] bytes = new byte[permList.size() / 8 + 1];
  		for (String p : perms) {
  			int idx = permList.indexOf(p);
  			if (idx > 0) {
  				int i = idx / 8;
  				idx = (int) Math.pow(2, idx - i * 8);
  				bytes[i] = (byte) (bytes[i] | idx);
  			}
  		}
  		return bytes;
  	}
  
  	private static final ArrayList<String> permList = new ArrayList<String>();
  
  	private static void populatePermList() {
  		final String[] permArray = { "android.permission.ACCESS_CHECKIN_PROPERTIES",
  				"android.permission.ACCESS_COARSE_LOCATION", "android.permission.ACCESS_FINE_LOCATION",
  				"android.permission.ACCESS_LOCATION_EXTRA_COMMANDS", "android.permission.ACCESS_MOCK_LOCATION",
  				"android.permission.ACCESS_NETWORK_STATE", "android.permission.ACCESS_SURFACE_FLINGER",
  				"android.permission.ACCESS_WIFI_STATE", "android.permission.ACCOUNT_MANAGER",
  				"android.permission.AUTHENTICATE_ACCOUNTS", "android.permission.BATTERY_STATS",
  				"android.permission.BIND_APPWIDGET", "android.permission.BIND_DEVICE_ADMIN",
  				"android.permission.BIND_INPUT_METHOD", "android.permission.BIND_WALLPAPER",
  				"android.permission.BLUETOOTH", "android.permission.BLUETOOTH_ADMIN", "android.permission.BRICK",
  				"android.permission.BROADCAST_PACKAGE_REMOVED", "android.permission.BROADCAST_SMS",
  				"android.permission.BROADCAST_STICKY", "android.permission.BROADCAST_WAP_PUSH",
  				"android.permission.CALL_PHONE", "android.permission.CALL_PRIVILEGED", "android.permission.CAMERA",
  				"android.permission.CHANGE_COMPONENT_ENABLED_STATE", "android.permission.CHANGE_CONFIGURATION",
  				"android.permission.CHANGE_NETWORK_STATE", "android.permission.CHANGE_WIFI_MULTICAST_STATE",
  				"android.permission.CHANGE_WIFI_STATE", "android.permission.CLEAR_APP_CACHE",
  				"android.permission.CLEAR_APP_USER_DATA", "android.permission.CONTROL_LOCATION_UPDATES",
  				"android.permission.DELETE_CACHE_FILES", "android.permission.DELETE_PACKAGES",
  				"android.permission.DEVICE_POWER", "android.permission.DIAGNOSTIC",
  				"android.permission.DISABLE_KEYGUARD", "android.permission.DUMP",
  				"android.permission.EXPAND_STATUS_BAR", "android.permission.FACTORY_TEST",
  				"android.permission.FLASHLIGHT", "android.permission.FORCE_BACK", "android.permission.GET_ACCOUNTS",
  				"android.permission.GET_PACKAGE_SIZE", "android.permission.GET_TASKS",
  				"android.permission.GLOBAL_SEARCH", "android.permission.HARDWARE_TEST",
  				"android.permission.INJECT_EVENTS", "android.permission.INSTALL_LOCATION_PROVIDER",
  				"android.permission.INSTALL_PACKAGES", "android.permission.INTERNAL_SYSTEM_WINDOW",
  				"android.permission.INTERNET", "android.permission.KILL_BACKGROUND_PROCESSES",
  				"android.permission.MANAGE_ACCOUNTS", "android.permission.MANAGE_APP_TOKENS",
  				"android.permission.MASTER_CLEAR", "android.permission.MODIFY_AUDIO_SETTINGS",
  				"android.permission.MODIFY_PHONE_STATE", "android.permission.MOUNT_FORMAT_FILESYSTEMS",
  				"android.permission.MOUNT_UNMOUNT_FILESYSTEMS", "android.permission.PERSISTENT_ACTIVITY",
  				"android.permission.PROCESS_OUTGOING_CALLS", "android.permission.READ_CALENDAR",
  				"android.permission.READ_CONTACTS", "android.permission.READ_FRAME_BUFFER",
  				"com.android.browser.permission.READ_HISTORY_BOOKMARKS", "android.permission.READ_INPUT_STATE",
  				"android.permission.READ_LOGS", "android.permission.READ_OWNER_DATA",
  				"android.permission.READ_PHONE_STATE", "android.permission.READ_SMS",
  				"android.permission.READ_SYNC_SETTINGS", "android.permission.READ_SYNC_STATS",
  				"android.permission.REBOOT", "android.permission.RECEIVE_BOOT_COMPLETED",
  				"android.permission.RECEIVE_MMS", "android.permission.RECEIVE_SMS",
  				"android.permission.RECEIVE_WAP_PUSH", "android.permission.RECORD_AUDIO",
  				"android.permission.REORDER_TASKS", "android.permission.RESTART_PACKAGES",
  				"android.permission.SEND_SMS", "android.permission.SET_ACTIVITY_WATCHER",
  				"android.permission.SET_ALWAYS_FINISH", "android.permission.SET_ANIMATION_SCALE",
  				"android.permission.SET_DEBUG_APP", "android.permission.SET_ORIENTATION",
  				"android.permission.SET_PREFERRED_APPLICATIONS", "android.permission.SET_PROCESS_LIMIT",
  				"android.permission.SET_TIME", "android.permission.SET_TIME_ZONE", "android.permission.SET_WALLPAPER",
  				"android.permission.SET_WALLPAPER_HINTS", "android.permission.SIGNAL_PERSISTENT_PROCESSES",
  				"android.permission.STATUS_BAR", "android.permission.SUBSCRIBED_FEEDS_READ",
  				"android.permission.SUBSCRIBED_FEEDS_WRITE", "android.permission.SYSTEM_ALERT_WINDOW",
  				"android.permission.UPDATE_DEVICE_STATS", "android.permission.USE_CREDENTIALS",
  				"android.permission.VIBRATE", "android.permission.WAKE_LOCK", "android.permission.WRITE_APN_SETTINGS",
  				"android.permission.WRITE_CALENDAR", "android.permission.WRITE_CONTACTS",
  				"android.permission.WRITE_EXTERNAL_STORAGE", "android.permission.WRITE_GSERVICES",
  				"com.android.browser.permission.WRITE_HISTORY_BOOKMARKS", "android.permission.WRITE_OWNER_DATA",
  				"android.permission.WRITE_SECURE_SETTINGS", "android.permission.WRITE_SETTINGS",
  				"android.permission.WRITE_SMS", "android.permission.WRITE_SYNC_SETTINGS" };
  
  		for (String s : permArray)
  			permList.add(s);
  	}
  }
  