public class InformaService extends Service implements SuckerCacheListener {
	private final IBinder binder = new LocalBinder();
	
	private long startTime = 0L;
	private long realStartTime = 0L;

	private int GPS_WAITING = 0;

	private SensorLogger<GeoSucker> _geo;
	private SensorLogger<PhoneSucker> _phone;
	private SensorLogger<AccelerometerSucker> _acc;
	private SensorLogger<EnvironmentalSucker> _env;
    private SensorLogger<DeviceSucker> _dev;
	private boolean suckersActive = false;

	private info.guardianproject.iocipher.File cacheFile, cacheRoot;
	private List<String> cacheFiles = new ArrayList<String>();
	private LoadingCache<Long, ILogPack> cache = null;
	
	private final static long CACHE_MAX = 500;
	private Timer cacheTimer;
	
	InformaCam informaCam;
	
	Handler h = new Handler();
	String associatedMedia = null;
	
	Intent stopIntent = new Intent().setAction(Actions.INFORMA_STOP);

	private static InformaService mInstance = null;

	public final static String ACTION_START_SUCKERS = "startsuckers";
	public final static String ACTION_STOP_SUCKERS = "stopsuckers";
	public final static String ACTION_RESET_CACHE = "resetcache";	
	
	private InformaBroadcaster[] broadcasters = {
			new InformaBroadcaster(new IntentFilter(BluetoothDevice.ACTION_FOUND)),
			new InformaBroadcaster(new IntentFilter(WifiManager.SCAN_RESULTS_AVAILABLE_ACTION))
	};

	private final static String LOG = App.Informa.LOG;

	public class LocalBinder extends Binder {
		public InformaService getService() {
			return InformaService.this;
		}
	}

	@Override
	public IBinder onBind(Intent intent) {
		return binder;
	}

	public static InformaService getInstance ()
	{
		return mInstance;
	}
	
	@Override
	public void onCreate() {
		Log.d(LOG, "started.");
		
		if (Debug.WAIT_FOR_DEBUGGER)
			android.os.Debug.waitForDebugger();
		
		mInstance = this;
		informaCam =  (InformaCam)getApplication();

		if (informaCam.ioService == null || (!informaCam.ioService.isMounted()))
		{
			//this seems like an auto-restart; we should stop
			stopSelf();
			return;
		}
		
		cacheRoot = new info.guardianproject.iocipher.File(IManifest.CACHES);
		if(!cacheRoot.exists()) {
			cacheRoot.mkdir();
		}

		sendBroadcast(new Intent()
			.putExtra(Codes.Keys.SERVICE, Codes.Routes.INFORMA_SERVICE)
			.setAction(Actions.ASSOCIATE_SERVICE)
			.putExtra(Codes.Extras.RESTRICT_TO_PROCESS, android.os.Process.myPid()));

		init();

	}
	
	
	@Override
	public int onStartCommand(Intent intent, int flags, int startId) {
		
		if (intent != null && intent.getAction()!=null)
		{
			if (intent.getAction().equals(ACTION_START_SUCKERS))
			{
				this.startAllSuckers();
			}
			else if (intent.getAction().equals(ACTION_STOP_SUCKERS))
			{
				this.stopAllSuckers();
			}
			else if (intent.getAction().equals(ACTION_RESET_CACHE))
			{
				resetCacheFiles();
			}
		}
		
		return START_STICKY;//super.onStartCommand(intent, flags, startId);
		
		
	}
	
	
	

	/**
	* Gets the state of Airplane Mode.
	* 
	* @param context
	* @return true if enabled.
	*/
	private static boolean isAirplaneModeOn(Context context) {

	   return Settings.System.getInt(context.getContentResolver(),
	           Settings.Global.AIRPLANE_MODE_ON, 0) != 0;

	}
	
	public ILocation getCurrentLocation() {
		if (_geo != null)
		{
			double[] dLoc = ((GeoSucker) _geo).updateLocation();
			
			if (dLoc != null)
				return new ILocation(dLoc);
			
		}
		 
		return null;
	}

	public long getCurrentTime() {
		//return System.currentTimeMillis() + (realStartTime == 0 ? 0 : (startTime - realStartTime));
		return System.currentTimeMillis();
	}
	
	public long getTimeOffset() {
		return realStartTime == 0 ? 0 : (startTime - realStartTime);
	}

	public void associateMedia(IMedia media) {
		this.associatedMedia = media._id;
	}
	
	public void unassociateMedia() {
		this.associatedMedia = null;
	}
	
	private void init() {
		h.post(new Runnable() {
			@Override
			public void run() {


				startTime = System.currentTimeMillis();
				long currentTime = 0;

				if (_geo != null)
				{
					currentTime = ((GeoSucker) _geo).getTime();

					if(currentTime != 0) {
						realStartTime = currentTime;
					}

					double[] currentLocation = ((GeoSucker) _geo).updateLocation();

					if(currentTime == 0 || currentLocation == null) {
						GPS_WAITING++;

						if(GPS_WAITING < Suckers.GPS_WAIT_MAX) {
							h.postDelayed(this, 200);
							return;
						} else {
						//Don't show notifications the user can't do anything about
							//Toast.makeText(InformaService.this, getString(R.string.gps_not_available_your), Toast.LENGTH_LONG).show();
							GPS_WAITING = 0; //reset
						}

					}

					onUpdate(((GeoSucker) _geo).forceReturn());
				}

				if (_phone != null)
				{

					onUpdate(((PhoneSucker) _phone).forceReturn());

				}

				if (informaCam != null)
				{
					sendBroadcast(new Intent()
						.setAction(Actions.INFORMA_START)
						.putExtra(Codes.Extras.RESTRICT_TO_PROCESS, informaCam.getProcess()));
				}
			}
		});

	}
	
	public void flushCache() {
		flushCache(null);
	}
	
	public void flushCache(IMedia m) {
		saveCache(true, m);
	}

	private void initCache() {
		try {
			cacheFile = new info.guardianproject.iocipher.File(cacheRoot, MediaHasher.hash(new String(startTime + "_" + System.currentTimeMillis()).getBytes(), "MD5"));
			cacheFiles = new ArrayList<String>();
			cacheFiles.add(cacheFile.getAbsolutePath());
		} catch (NoSuchAlgorithmException e) {
			Logger.e(LOG, e);
		} catch (IOException e) {
			Logger.e(LOG, e);
		}
		
		cacheTimer = new Timer();
		cacheTimer.schedule(new TimerTask() {

			@Override
			public void run() {
				if(cache != null) {
					
					if(cache.size() >= CACHE_MAX) {
						saveCache(true, null);
					}
				}
				
			}
			
		}, 0, 4000L);
		
		startTime = System.currentTimeMillis();
		cache = CacheBuilder.newBuilder()
				.build(new CacheLoader<Long, ILogPack>() {

					@Override
					public ILogPack load(Long timestamp) throws Exception {
						return cache.getUnchecked(timestamp);
					}

				});
		
	}
	
	private void saveCache() {
		saveCache(false, null);
	}

	private Thread mThread = null;
	
	private void saveCache(final boolean restartCache, final IMedia m) {	
		
		if (mThread == null || (!mThread.isAlive()))
		{

			if (cache == null) //service may have been stopped
				return;
			
			Log.d(LOG, "CACHE SIZE SO FAR: " + cache.size() + "\nSaving and restarting cache...");
			
			mThread = new Thread(new Runnable() {
				@Override
				public void run() {
					ISuckerCache suckerCache = new ISuckerCache();
					JSONArray cacheArray = new JSONArray();
	
					Iterator<Entry<Long, ILogPack>> cIt = cache.asMap().entrySet().iterator();
					while(cIt.hasNext()) {
						JSONObject cacheMap = new JSONObject();
						Entry<Long, ILogPack> c = cIt.next();
						try {
							cacheMap.put(String.valueOf(c.getKey()), c.getValue());
							cacheArray.put(cacheMap);
						} catch(JSONException e) {
							Logger.e(LOG, e);
						}
					}
									
					suckerCache.timeOffset = realStartTime;
					suckerCache.cache = cacheArray;
					
					// TODO: XXX: collision errors (ConcurrentModificationException)
					informaCam.ioService.saveBlob(suckerCache.asJson().toString().getBytes(), cacheFile);
	
					if(associatedMedia != null) {
						IMedia media = informaCam.mediaManifest.getById(associatedMedia);
						if(media.associatedCaches == null) {
							media.associatedCaches = new ArrayList<String>();
						}
	
						if(!media.associatedCaches.contains(cacheFile.getAbsolutePath())) { 
							media.associatedCaches.add(cacheFile.getAbsolutePath());
						}
	
						try
						{
				//		Logger.d(LOG, "OK-- I am about to save the cache reference.  is this still correct?\n" + media.asJson().toString());
							media.save();
						}
						catch (Exception e)
						{
							Logger.e(LOG, e);
							return;
						}
					}
					
					if(m != null) {
						associateMedia(m);
					}
					
					InformaService.this.onCacheSaved(restartCache);
				}
			});
			
			mThread.start();		
		}
		else
		{

			Log.d(LOG, "CACHE SAVE IN PROGRESS... WAITING IN LINE ...");
		}
		
	}

	public boolean suckersActive ()
	{
		return suckersActive;
	}
		
	private void startAllSuckers() {

		if(suckersActive) {
			return;
		}
		
		Logger.d(LOG, "STARTING INFORMA SUCKERS...");
		for(BroadcastReceiver broadcaster : broadcasters) {
			this.registerReceiver(broadcaster, ((InformaBroadcaster) broadcaster).intentFilter);
		}
		
		initCache();
		
		boolean prefGpsEnableHires = PreferenceManager.getDefaultSharedPreferences(this.getApplicationContext()).getBoolean("prefGpsEnableHires",false);
		boolean hasPlayServices = GooglePlayServicesUtil.isGooglePlayServicesAvailable(getApplicationContext()) == ConnectionResult.SUCCESS;
	
		if (prefGpsEnableHires || (!hasPlayServices))
			_geo = new GeoHiResSucker(this);
		else	
			_geo = new GeoFusedSucker(this);
		
		_geo.setSuckerCacheListener(this);
		
		_phone = new PhoneSucker(this);
		_phone.setSuckerCacheListener(this);
	
		_acc = new AccelerometerSucker(this);
		_acc.setSuckerCacheListener(this);
		
		_env = new EnvironmentalSucker(this);
		_env.setSuckerCacheListener(this);

        _dev = new DeviceSucker(this);
        _dev.setSuckerCacheListener(this);
		
		try {
			
			double[] dLoc = ((GeoSucker) _geo).updateLocation();
			if (dLoc != null)
				((EnvironmentalSucker)_env).updateSeaLevelPressure(dLoc[0], dLoc[1]);
			
		} catch (Exception e) {
			Log.d(Informa.LOG,"error updating sea level pressure",e);
		}
		
		
		suckersActive = true;
		
		showNotification();
	}
	
	
	private void showNotification ()
	{

		  Intent notificationIntent = new Intent(this, AlwaysOnActivity.class);
		    PendingIntent pendingIntent=PendingIntent.getActivity(this, 0,
		            notificationIntent, Intent.FLAG_ACTIVITY_NEW_TASK);

		    Notification notification=new NotificationCompat.Builder(this)
		                                .setSmallIcon(R.drawable.ic_action_camera)
		                                .setContentTitle(getString(R.string.proof_mode_activated))
		                                .setContentIntent(pendingIntent)
		                                .setOngoing(true).build();
		    
		    startForeground(991199, notification);
		    	        
	}
	
	private void stopAllSuckers() {
		if(!suckersActive) {
			return;
		}
		
		Logger.d(LOG, "STOPPING INFORMA SUCKERS...");
		saveCache();

		if (_phone != null)
			_phone.getSucker().stopUpdates();
		
		if (_acc != null)
			_acc.getSucker().stopUpdates();
		
		if (_geo != null)
			_geo.getSucker().stopUpdates();
		
		if (_env != null)
			_env.getSucker().stopUpdates();

        if (_dev != null)
            _dev.getSucker().stopUpdates();

		_geo = null;
		_phone = null;
		_acc = null;
		_env = null;
        _dev = null;

		for(BroadcastReceiver b : broadcasters) {
			
			try
			{
				unregisterReceiver(b);
			}
			catch (IllegalArgumentException iae)
			{
				//some broadcasters may not be registered; don't let this stop us from getting destroyed!
			}
		}
		
		suckersActive = false;

		stopForeground(true);
	}
	
	@Override
	public void onDestroy() {
		super.onDestroy();

		stopAllSuckers();

		sendBroadcast(stopIntent.putExtra(Codes.Extras.RESTRICT_TO_PROCESS, informaCam.getProcess()));
		
		sendBroadcast(new Intent()
			.putExtra(Codes.Keys.SERVICE, Codes.Routes.INFORMA_SERVICE)
			.setAction(Actions.DISASSOCIATE_SERVICE)
			.putExtra(Codes.Extras.RESTRICT_TO_PROCESS, android.os.Process.myPid()));
	}


	public List<ILogPack> getAllEventsByType(final int type) throws InterruptedException, ExecutionException, JSONException {
		Iterator<Entry<Long, ILogPack>> cIt = cache.asMap().entrySet().iterator();
		List<ILogPack> events = new ArrayList<ILogPack>();
		while(cIt.hasNext()) {
			Entry<Long, ILogPack> entry = cIt.next();
			if(entry.getValue().has(CaptureEvent.Keys.TYPE) && entry.getValue().getInt(CaptureEvent.Keys.TYPE) == type)
				events.add(entry.getValue());
		}

		return events;
	}

	public List<Entry<Long, ILogPack>> getAllEventsByTypeWithTimestamp(final int type) throws JSONException, InterruptedException, ExecutionException {
		Iterator<Entry<Long, ILogPack>> cIt = cache.asMap().entrySet().iterator();
		List<Entry<Long, ILogPack>> events = new ArrayList<Entry<Long, ILogPack>>();
		while(cIt.hasNext()) {
			Entry<Long, ILogPack> entry = cIt.next();
			if(entry.getValue().has(CaptureEvent.Keys.TYPE) && entry.getValue().getInt(CaptureEvent.Keys.TYPE) == type)
				events.add(entry);
		}

		return events;
	}

	public Entry<Long, ILogPack> getEventByTypeWithTimestamp(final int type) throws JSONException, InterruptedException, ExecutionException {
		Iterator<Entry<Long, ILogPack>> cIt = cache.asMap().entrySet().iterator();
		Entry<Long, ILogPack> entry = null;
		while(cIt.hasNext() && entry == null) {
			Entry<Long, ILogPack> e = cIt.next();
			if(e.getValue().has(CaptureEvent.Keys.TYPE) && e.getValue().getInt(CaptureEvent.Keys.TYPE) == type)
				entry = e;
		}

		return entry;
	}

	public ILogPack getEventByType(final int type) throws JSONException, InterruptedException, ExecutionException {
		Iterator<ILogPack> cIt = cache.asMap().values().iterator();
		ILogPack ILogPack = null;
		while(cIt.hasNext() && ILogPack == null) {
			ILogPack lp = cIt.next();

			if(lp.has(CaptureEvent.Keys.TYPE) && lp.getInt(CaptureEvent.Keys.TYPE) == type)
				ILogPack = lp;
		}

		return ILogPack;
	}

	@SuppressWarnings("unchecked")
	public boolean removeRegion(IRegion region) {
		try { 
			ILogPack ILogPack = cache.getIfPresent(region.timestamp);
			if(ILogPack.has(CaptureEvent.Keys.TYPE) && ILogPack.getInt(CaptureEvent.Keys.TYPE) == CaptureEvent.REGION_GENERATED) {
				ILogPack.remove(CaptureEvent.Keys.TYPE);
			}

			Iterator<String> repIt = region.asJson().keys();
			while(repIt.hasNext()) {
				ILogPack.remove(repIt.next());
			}

			return true;
		} catch(NullPointerException e) {
			Log.e(LOG, e.toString());
			e.printStackTrace();
		} catch (Exception e) {
			Log.e(LOG, e.toString(),e);
		}

		return false;
	}

	@SuppressWarnings("unchecked")
	public void addRegion(IRegion region) {
		ILogPack ILogPack = new ILogPack(CaptureEvent.Keys.TYPE, CaptureEvent.REGION_GENERATED, true);
		
		if (_geo != null)
		{
			ILogPack regionLocationData = ((GeoSucker) _geo).forceReturn();
			try {
				ILogPack.put(CaptureEvent.Keys.REGION_LOCATION_DATA, regionLocationData);
			} catch (Exception e) {
				Log.e(LOG, e.toString(),e);
			}
		}
		
		Iterator<String> rIt = region.asJson().keys();
		while(rIt.hasNext()) {
			String key = rIt.next();
			try {
				ILogPack.put(key, region.asJson().get(key));
			} catch (Exception e) {
				Log.e(LOG, e.toString(),e);
			}
		}

		//Log.d(LOG, "HEY NEW REGION: " + ILogPack.asJson().toString());
		region.timestamp = onUpdate(ILogPack);
	}

	@SuppressWarnings("unchecked")
	public void updateRegion(IRegion region) {
		try {
			ILogPack ILogPack = cache.getIfPresent(region.timestamp);
			Iterator<String> repIt = region.asJson().keys();
			while(repIt.hasNext()) {
				String key = repIt.next();
				ILogPack.put(key, region.asJson().get(key));
			}
		} catch(JSONException e) {
			Log.e(LOG, e.toString(),e);
		} catch(NullPointerException e) {
			Log.e(LOG, "CONSIDERED HANDLED:\n" + e.toString(),e);
			
			addRegion(region);
		}

	}

	@SuppressWarnings({ "unchecked", "unused" })
	private ILogPack JSONObjectToILogPack(JSONObject json) throws JSONException {
		ILogPack ILogPack = new ILogPack();
		Iterator<String> jIt = json.keys();
		while(jIt.hasNext()) {
			String key = jIt.next();
			ILogPack.put(key, json.get(key));
		}
		return ILogPack;
	}

	@SuppressWarnings("unused")
	private void pushToSucker(SensorLogger<?> sucker, ILogPack ILogPack) throws JSONException {
		if(sucker.getClass().equals(PhoneSucker.class))
			_phone.sendToBuffer(ILogPack);
	}
	
	public String getCacheFile() {
		return cacheFile.getAbsolutePath();
	}
	
	public List<String> getCacheFiles() {			
		return cacheFiles;
	}
	
	private void resetCacheFiles ()
	{
		if (cacheFiles.size() > 0)
		{
			String lastCache = cacheFiles.get(cacheFiles.size()-1);
			cacheFiles = new ArrayList<String>();
			cacheFiles.add(lastCache);
		}
	}

	@SuppressWarnings("unchecked")
	@Override
	public void onUpdate(long timestamp, ILogPack iLogPack) {
		try {
			if (cache == null)
				initCache();
			
			ILogPack lp = cache.getIfPresent(timestamp);
			if(lp != null) {
				synchronized(iLogPack) //lock access to lp so it is not modified
				{
					Iterator<String> lIt = lp.keys();
					while(lIt.hasNext()) {
						String key = lIt.next();
						iLogPack.put(key, lp.get(key));	
					}
				}
			}

			cache.put(timestamp, iLogPack);
		} catch(JSONException e) {}
	}

	@Override
	public long onUpdate(ILogPack ILogPack) {
		long timestamp = getCurrentTime();
		onUpdate(timestamp, ILogPack);
		
		return timestamp;
	}

	class InformaBroadcaster extends BroadcastReceiver {
		IntentFilter intentFilter;

		public InformaBroadcaster(IntentFilter intentFilter) {
			this.intentFilter = intentFilter;
		}

		@Override
		public void onReceive(Context context, Intent intent) {
			
			if (_phone != null)
			{
				if(intent.getAction().equals(BluetoothDevice.ACTION_FOUND)) {
					try {
						BluetoothDevice bd = (BluetoothDevice) intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
						ILogPack logPack = new ILogPack(Phone.Keys.BLUETOOTH_DEVICE_ADDRESS, bd.getAddress());					
						logPack.put(Phone.Keys.BLUETOOTH_DEVICE_NAME, bd.getName());					
						onUpdate(logPack);
	
					} catch(JSONException e) {}
	
				} else if(intent.getAction().equals(WifiManager.SCAN_RESULTS_AVAILABLE_ACTION)) {
					try {
						ILogPack ILogPack = new ILogPack(Phone.Keys.VISIBLE_WIFI_NETWORKS, ((PhoneSucker) _phone).getWifiNetworks());
						onUpdate(ILogPack);
					} catch(NullPointerException e) {
						Log.e(LOG, "CONSIDERED HANDLED:\n" + e.toString());
						e.printStackTrace();
					}
	
				}
			}
		}

	}

	private void onCacheSaved(boolean restartCache) {
		cacheTimer.cancel();
		
		if(restartCache) {
			initCache();
		}
	}
} @SuppressLint("DefaultLocale")
public class IDCIMDescriptor extends Model {	
	public List<IDCIMEntry> shortDescription = new ArrayList<IDCIMEntry>();
	public Hashtable<String,IDCIMEntry> intakeMasterList = new Hashtable<String,IDCIMEntry>();

	private long startTime = 0L;
	private long timeOffset = 0L;
	private String parentId = null;
	private InformaCam informaCam = InformaCam.getInstance();
	private String cameraComponent = null;

	private final static String LOG = Storage.LOG;

	public IDCIMDescriptor(String parentId, ComponentName cameraComponent) {
        startTime = System.currentTimeMillis() / 1000;
        this.parentId = parentId;

        if (cameraComponent != null) {
            this.cameraComponent = cameraComponent.getPackageName();
            Logger.d(LOG, "CAMERA COMPONENT: " + this.cameraComponent);
        }
	}

	public IDCIMSerializable asDescriptor() {
		return new IDCIMSerializable(shortDescription);
	}

	public void addEntry(String path, boolean isThumbnail, int sourceType) throws InstantiationException, IllegalAccessException {
		final IDCIMEntry entry = new IDCIMEntry();
		
		Uri authority = Uri.parse(path);
		entry.authority = authority.toString();
		
		String sortBy = "date_added DESC";

		if(isThumbnail) {
			sortBy = null;
			entry.mediaType = Models.IDCIMEntry.THUMBNAIL;
		}

		Cursor cursor = null;
		
		if (sourceType !=Storage.Type.IOCIPHER) 
			cursor = InformaCam.getInstance().getContentResolver().query(authority, null, null, null, sortBy);

		if(cursor != null && cursor.moveToFirst()) {
			path = cursor.getString(cursor.getColumnIndexOrThrow(MediaColumns.DATA));
			
			/*
			 * IF the path is not already in out dcimList
			 */
			
			if (intakeMasterList.containsKey(path))
			{
				cursor.close();
				return;
			}
			else
				intakeMasterList.put(path, entry);
			
			entry.fileAsset = new IAsset(path, sourceType);

			if(!isThumbnail) {
				entry.timeCaptured = cursor.getLong(cursor.getColumnIndexOrThrow(MediaColumns.DATE_ADDED));
				if(entry.timeCaptured < startTime) {
				//	Logger.d(LOG, "this media occured too early to count");
					cursor.close();

					return;
				}

				entry.mediaType = cursor.getString(cursor.getColumnIndexOrThrow(MediaColumns.MIME_TYPE));
				entry.cameraComponent = cameraComponent;

				//if(entry.mediaType.equals(MimeType.VIDEO_3GPP)) {
				//entry.mediaType = MimeType.VIDEO;
				//}
			}

			// String pattern = "^([a-zA-Z0-9]+)([a-zA-Z0-9_]*)\\.(jpg|mp4){1}$";
			
			entry.id = cursor.getLong(cursor.getColumnIndexOrThrow(MediaColumns._ID));
			cursor.close();
			
			if(!isThumbnail) {
				IDCIMEntry clone = new IDCIMEntry(entry);

				if(!shortDescription.contains(clone)) {
					shortDescription.add(clone);
				}
			}
			
			entry.exif = new IExif();
		
			if (InformaService.getInstance() != null && InformaService.getInstance().getCurrentLocation() != null)
			{
				entry.exif.location = InformaService.getInstance().getCurrentLocation().geoCoordinates;
			}


			List<IDCIMEntry> intakeList = new ArrayList<IDCIMEntry>();
			intakeList.add(entry);
						
			InformaCam informaCam = InformaCam.getInstance();

			Intent intakeIntent = new Intent(informaCam, Intake.class);

			
			if (InformaService.getInstance() != null)
			{
				List<String> cacheFiles = InformaService.getInstance().getCacheFiles();
				intakeIntent.putExtra(Codes.Extras.INFORMA_CACHE, cacheFiles.toArray(new String[cacheFiles.size()]));

				Intent intentSuckers = new Intent(informaCam, InformaService.class);
				intentSuckers.setAction(InformaService.ACTION_RESET_CACHE);
				informaCam.startService(intentSuckers);
			}
			
			intakeIntent.putExtra(Codes.Extras.RETURNED_MEDIA, new IDCIMSerializable(intakeList));
			intakeIntent.putExtra(Codes.Extras.TIME_OFFSET, timeOffset);
			if(parentId != null) {
				intakeIntent.putExtra(Codes.Extras.MEDIA_PARENT, parentId);
			}

			informaCam.startService(intakeIntent);
			
			
		}
		else
		{
			
			
			if (intakeMasterList.containsKey(path))
			{
				return; //we got it already
			}
			else
				intakeMasterList.put(path, entry);
			
			entry.fileAsset = new IAsset(path, sourceType);

			if(!isThumbnail) {
				entry.timeCaptured = new java.util.Date().getTime();
				if(entry.timeCaptured < startTime) {
					Logger.d(LOG, "this media occured too early to count");
					cursor.close();

					return;
				}

				entry.mediaType = MimeType.IMAGE;
				if (path.endsWith("mp4")||path.endsWith("ts")||path.endsWith("mov"))
					entry.mediaType = MimeType.VIDEO_MP4;
				else if (path.endsWith("3gp"))
					entry.mediaType = MimeType.VIDEO_3GPP;
					
				entry.cameraComponent = cameraComponent;

				
			}

			if(!isThumbnail) {
				IDCIMEntry clone = new IDCIMEntry(entry);

				if(!shortDescription.contains(clone)) {
					shortDescription.add(clone);
				}
			}
			
			entry.exif = new IExif();
		
			if (InformaService.getInstance() != null && InformaService.getInstance().getCurrentLocation() != null)
			{
				entry.exif.location = InformaService.getInstance().getCurrentLocation().geoCoordinates;
			}


			List<IDCIMEntry> intakeList = new ArrayList<IDCIMEntry>();
			intakeList.add(entry);
						
			InformaCam informaCam = InformaCam.getInstance();

			Intent intakeIntent = new Intent(informaCam, Intake.class);

			intakeIntent.putExtra(Codes.Extras.RETURNED_MEDIA, new IDCIMSerializable(intakeList));
						
			List<String> cacheFiles = InformaService.getInstance().getCacheFiles();
			intakeIntent.putExtra(Codes.Extras.INFORMA_CACHE, cacheFiles.toArray(new String[cacheFiles.size()]));			

			Intent intentSuckers = new Intent(informaCam, InformaService.class);
			intentSuckers.setAction(InformaService.ACTION_RESET_CACHE);
			informaCam.startService(intentSuckers);
			
			intakeIntent.putExtra(Codes.Extras.TIME_OFFSET, timeOffset);
			if(parentId != null) {
				intakeIntent.putExtra(Codes.Extras.MEDIA_PARENT, parentId);
			}

			informaCam.startService(intakeIntent);
		}
		
		
	}

	public void startSession() {
		InformaCam informaCam = InformaCam.getInstance();
		timeOffset = InformaService.getInstance().getTimeOffset();
		
		Logger.d(LOG, "starting dcim session");
	}

	public void stopSession() {
		// start up intake queue
		/*
		if(!intakeList.isEmpty()) {
			InformaCam informaCam = InformaCam.getInstance();

			Intent intakeIntent = new Intent(informaCam, Intake.class);

			intakeIntent.putExtra(Codes.Extras.RETURNED_MEDIA, new IDCIMSerializable(intakeList));
			
			List<String> cacheFiles = InformaService.getInstance().getCacheFiles();
			intakeIntent.putExtra(Codes.Extras.INFORMA_CACHE, cacheFiles.toArray(new String[cacheFiles.size()]));
			
			intakeIntent.putExtra(Codes.Extras.TIME_OFFSET, timeOffset);
			if(parentId != null) {
				intakeIntent.putExtra(Codes.Extras.MEDIA_PARENT, parentId);
			}

			informaCam.startService(intakeIntent);
			Logger.d(LOG, "saved a dcim descriptor");
		} else {
			Logger.d(LOG, "there were no entries.");
		}*/
	}

	public static class IDCIMSerializable extends Model implements Serializable {
		private static final long serialVersionUID = 3688700992408456583L;
		
		public List<IDCIMEntry> dcimList;

		public IDCIMSerializable(List<IDCIMEntry> dcimList) {
			super();
			this.dcimList = dcimList;
		}
	}
} public final static class Actions {
	public final static String INIT = "org.witness.informacam.action.INIT";
	public final static String SHUTDOWN = "org.witness.informacam.action.SHUTDOWN";
	public final static String ASSOCIATE_SERVICE = "org.witness.informacam.action.ASSOCIATE_SERVICE";
	public static final String DISASSOCIATE_SERVICE = "org.witness.informacam.action.DISASSOCIATE_SERVICE";
	public static final String UPLOADER_UPDATE = "org.witness.informacam.action.UPLOADER_UPDATE";
	public static final String CAMERA = "android.media.action.IMAGE_CAPTURE";
	public static final String INFORMACAM_START = "org.witness.informacam.action.INFORMACAM_START";
	public static final String INFORMACAM_STOP = "org.witness.informacam.action.INFORMACAM_STOP";
	public static final String INFORMA_START = "org.witness.informacam.action.INFORMA_SERVICE_START";
	public static final String INFORMA_STOP = "org.witness.informacam.action.INFORMA_SERVICE_STOP";
	public static final String PERSISTENT_SERVICE = "org.witness.informacam.action.PERSISTENT_SERVICE";
	public static final String VERIFIED_MOBILE_MEDIA = "info.guardianproject.action.VERIFIED_MOBILE_MEDIA";
	public static final String USER_ACCEPT_ACTION = "org.witness.informacam.action.USER_ACCEPT_ACTION";
		
	public static final String[] OUTSIDE_THE_LOOP = new String[] {
		VERIFIED_MOBILE_MEDIA,
		CAMERA
	};
} public final static class App {
	public final static String LOG = "InformaMain";

	public static final class Camera {
		public final static String LOG = "InformaCamera";
		public final static String TYPE = "cameraType";

		public static final class Type {
			public final static int CAMERA = 500;
			public final static int CAMCORDER = 501;
			public final static int USERCONTROLLED = 502;
			public final static int SECURE_CAMERA = 503;
			public final static int SECURE_CAMCORDER = 504;
		}

		public static final class Intents {
			public final static String CAMERA = MediaStore.INTENT_ACTION_STILL_IMAGE_CAMERA;
			public final static String CAMCORDER = MediaStore.INTENT_ACTION_VIDEO_CAMERA;
			public final static String SECURE_CAMERA =  "info.guardianproject.action.SECURE_STILL_IMAGE_CAMERA";
			public final static String SECURE_CAMCORDER =  "info.guardianproject.action.SECURE_VIDEO_CAMERA";
		//	public final static String CAMERA_SIMPLE = MediaStore.INTENT_ACTION_STILL_IMAGE_CAMERA;
		}

		public static final class Authority {
			public final static Uri CAMERA = MediaStore.Images.Media.EXTERNAL_CONTENT_URI;
			public final static Uri CAMCORDER = MediaStore.Video.Media.EXTERNAL_CONTENT_URI;
		}

		public static final String TAG = "InformaCam.Camera";

		public final static List<String> SUPPORTED;
		static {
			List<String> supported = new Vector<String>();
			supported.add("org.witness.informacam");
			supported.add("com.sec.android.app.camera");
			supported.add("com.android.camera");
			supported.add("com.google.android.gallery3d");
			supported.add("com.google.android.GoogleCamera");
			supported.add("com.motorola.camera");
			SUPPORTED = Collections.unmodifiableList(supported);
		}
	}

	public final static class Transport {
		public final static String LOG = "InformaTransport";
			
		public final static class Results {
			public final static String OK_BUT_FAIL = "500: Internal Server Error";
			public final static String OK = "200";
			public final static String[] FAIL = {"404", "500"};
		}
	}

	public final static class Background {
		public final static String LOG = "InformaBackground";
	}
		
	public final static class Forms {
		public static final String FREE_AUDIO = "iWitness Free Audio Annotation";
		public static final String FREE_TEXT = "iWitness Free Text Annotations";
	}
		
	public final static class Storage {
		public final static String LOG = "InformaStorage";
		public static final String ROOT = "informaCamIOCipher";
		public static final String IOCIPHER = "ic_data.db";
		public static final String DUMP = "informaCam";

		//public static final String EXTERNAL_DIR = "/sdcard/InformaCam";//Environment.getExternalStorageDirectory().getAbsolutePath() + "/InformaCam";
		public static final String FORM_ROOT = "forms";
		public static final String ORGS_ROOT = "organizations";
		public static final String LOG_DUMP = "iLogs";
		public static final String ATTACHED_MEDIA = "attachedMedia";
		//public static final String DCIM = "/storage/extSdCard/DCIM/Camera";

           // external dirs should not be hard-coded (harlo 10/7/15)
           public static final String EXTERNAL_DIR = String.format("%s/CameraV", Environment.getExternalStorageDirectory().getAbsolutePath());
           public static final String DCIM = String.format("%s/Camera", Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DCIM).getAbsolutePath());

		public final static class Type {

			public static final int INTERNAL_STORAGE = 200;
			public static final int IOCIPHER = 201;
			public static final int APPLICATION_ASSET = 202;
			public static final int CONTENT_RESOLVER = 203;
			public static final int FILE_SYSTEM = 204;

		}
			
		public final static class Intake {
			public final static String TAG = "dcimIntake";
		}
			
		public final static class ICTD {
			public final static List<String> ZIP_OMITABLES;
			static {
				List<String> zip_omitables = new ArrayList<String>();
				zip_omitables.add("__MACOSX");
				zip_omitables.add("DS_Store");
				ZIP_OMITABLES = Collections.unmodifiableList(zip_omitables);
			}
		}
	}

	public final static class Informa {
		public final static String LOG = "InformaCore";
	}

	public final static class Crypto {
		public final static String LOG = "InformaCrypto";

		public final static byte[] REGION_SALT = {(byte) 0xC4, (byte) 0xE2, (byte) 0xA4, 
			(byte) 0xF2, (byte) 0xEA, (byte) 0xA0, (byte) 0xBE, (byte) 0xF7};
		public final static byte[] FORM_SALT = {(byte) 0x70, (byte) 0xB4, (byte) 0xEE,
			(byte) 0x9B, (byte) 0xD3, (byte) 0x80, (byte) 0xEC, (byte) 0x74};

		public final static class Signatures {
			public final static class Keys {
				public final static String SIGNATURE = "dataSignature";
			}
		}
	}

	public final static class ImageCapture {
		public final static String LOG = "InformaCapture";
		public final static int ROUTE = Codes.Routes.IMAGE_CAPTURE;
	}
} public final static class Informa {
	public final static String LOG = "InformaCore";
} public final static class Storage {
	public final static String LOG = "InformaStorage";
	public static final String ROOT = "informaCamIOCipher";
	public static final String IOCIPHER = "ic_data.db";
	public static final String DUMP = "informaCam";

	//public static final String EXTERNAL_DIR = "/sdcard/InformaCam";//Environment.getExternalStorageDirectory().getAbsolutePath() + "/InformaCam";
	public static final String FORM_ROOT = "forms";
	public static final String ORGS_ROOT = "organizations";
	public static final String LOG_DUMP = "iLogs";
	public static final String ATTACHED_MEDIA = "attachedMedia";
	//public static final String DCIM = "/storage/extSdCard/DCIM/Camera";

          // external dirs should not be hard-coded (harlo 10/7/15)
          public static final String EXTERNAL_DIR = String.format("%s/CameraV", Environment.getExternalStorageDirectory().getAbsolutePath());
          public static final String DCIM = String.format("%s/Camera", Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DCIM).getAbsolutePath());

	public final static class Type {

		public static final int INTERNAL_STORAGE = 200;
		public static final int IOCIPHER = 201;
		public static final int APPLICATION_ASSET = 202;
		public static final int CONTENT_RESOLVER = 203;
		public static final int FILE_SYSTEM = 204;

	}
			
	public final static class Intake {
		public final static String TAG = "dcimIntake";
	}
			
	public final static class ICTD {
		public final static List<String> ZIP_OMITABLES;
		static {
			List<String> zip_omitables = new ArrayList<String>();
			zip_omitables.add("__MACOSX");
			zip_omitables.add("DS_Store");
			ZIP_OMITABLES = Collections.unmodifiableList(zip_omitables);
		}
	}
} public final static class Type {

	public static final int INTERNAL_STORAGE = 200;
	public static final int IOCIPHER = 201;
	public static final int APPLICATION_ASSET = 202;
	public static final int CONTENT_RESOLVER = 203;
	public static final int FILE_SYSTEM = 204;

} public final static class Codes {
	public final static class Routes {
		public final static int IMAGE_CAPTURE = 100;
		public final static int SIGNATURE_SERVICE = 101;
		public static final int IO_SERVICE = 102;
		public static final int UPLOADER_SERVICE = 103;
		public static final int RETRY_SAVE = 104;
		public static final int RETRY_GET = 105;
		public static final int INFORMA_SERVICE = 106;
		public static final int BACKGROUND_PROCESSOR = 107;
	}
		
	public final static class Authentication {
		public final static int REQUEST_ACCOUNT_PICKER = 200;
		public final static int REQUEST_AUTHORIZATION = 201;
	}
		
	public final static class Tasks {
		public final static int ANALYZE_MEDIA = 1;
	}
		
	public final static class Status {
		public final static int UNKNOWN = 0;
		public final static int UNINITIALIZED = 1;
		public final static int LOCKED = 2;
		public final static int UNLOCKED = 3;
	}

	public final static class Keys {
		public final static String SERVICE = "service";
		public static final String IV = "iv";
		public static final String VALUE = "value";
		public static final String UPLOADER = "uploader";
		public static final String DCIM_DESCRIPTOR = "dcimDescriptor";
		public static final String BATCH_EXPORT_FINISHED = "batchExportFinished";

		public static final class UI {
			public static final String PROGRESS = "progress";
			public static final String UPDATE = "update";
		}
	}

	public final static class Extras {
		public final static String WIZARD_SUPPLEMENT = "wizard_supplement";
		public static final String MESSAGE_CODE = "message_code";
		public static final String RETURNED_MEDIA = "informacam_returned_media";
		public static final String INSTALL_NEW_KEY = "install_ictd_uri";
		public static final String LOGOUT_USER = "logout_user";
		public static final String RESTRICT_TO_PROCESS = "restrict_to_process";
		public static final String CAMERA_TYPE = "camera_type";
		public static final String GPS_FAILURE = "gps_failure";
		public static final String SET_LOCALES = "set_locales";
		public static final String LOCALE_PREF_KEY = "locale_pref_key";
		public static final String CHANGE_LOCALE = "changeLocale";
		public static final String CONSOLIDATE_MEDIA = "consolidateMedia";
		public static final String MEDIA_PARENT = "mediaParent";
		public static final String GENERAL_FAILURE = "generalFailure";
		public static final String INFORMA_CACHE = "informaCacheFile";
		public static final String TIME_OFFSET = "informaTimeOffset";
		public static final String NUM_PROCESSING = "numProcessing";
		public static final String NUM_COMPLETED = "numCompleted";
		public static final String CRON_INTERVAL = "informaCronInterval";
	}

	public static final class Messages {
		public static final class Transport {
			public static final int GENERAL_FAILURE = 404;
			public static final int ORBOT_NOT_RUNNING = 405;
			public static final int ORBOT_UNINSTALLED = 406;
		}
			
		public static final class Wizard {
			public final static int INIT = 300;
		}

		public static final class UI {
			public final static int UPDATE = 301;
			public static final int REPLACE = 302;
		}

		public static final class Login {
			public final static int DO_LOGIN = 303;
			public final static int DO_LOGOUT = 304;
		}

		public static final class DCIM {
			public final static int START = 305;
			public final static int STOP = 306;
			public final static int ADD = 307;
			public final static int PENDING = 308;
		}

		public static final class Home {
			public final static int INIT = 309;
		}

	}

	public static final class Transport {

		public static final int MUST_INSTALL_TOR = 400;
		public static final int MUST_START_TOR = 401;
	}

	public class Media {
		public static final int ORIENTATION_PORTRAIT = 1;
		public static final int ORIENTATION_LANDSCAPE = 2;

		public static final int TYPE_IMAGE = 400;
		public static final int TYPE_VIDEO = 401;
		public static final int TYPE_JOURNAL = 402;
	}

	public class Adapters {
		public static final int ALL = 0;
		public static final int NOTIFICATIONS = 1;
		public static final int ORGANIZATIONS = 2;
	}
} public final static class IManifest {
	public final static String USER = "informacam_manifest";
	public static final String PREF = "informacam_preferences";		
	public final static String DCIM = "dcimDescriptor";
	public final static String MEDIA = "mediaManifest";
	public static final String FORMS = "installedForms";
	public static final String ORGS = "installedOrganizations";
	public static final String KEY_STORE_MANIFEST = "keystoreManifest";
	public static final String KEY_STORE = "keystore.jks";
	public static final String CACHES = "informaCaches";
	public static final String NOTIFICATIONS = "notificationsManifest";
	public static final String DEX = "dexDump";
	public static final String LANG = "languageMap";
	public static final String TRANSPORT = "transportManifest";
	public static final String ANON = "userAnon";
} public interface InformaCamEventListener {
	public void onUpdate(Message message);
} public interface ListAdapterListener {
	public void updateAdapter(int which);
	public void setPending(int numPending, int numCompleted);
} public final static class Logger {
	public Logger() {}
		
	public final static boolean DEBUG = true;
		
	public static void e(String LOG, ExceptionInInitializerError e) {
		Log.e(LOG, e.toString(),e);
		try {
			Log.e(LOG, e.getMessage());
		} catch(NullPointerException npe) {}
			
		e.printStackTrace();
	}
		
	public static void e(String LOG, Exception e) {
		Log.e(LOG, e.toString(),e);
			
	}
		
	public static void d(String LOG, String msg) {
		if (DEBUG)
			Log.d(LOG, msg);
	}
} public final static class Models {
	public static final String _ID = "_id";
	public static final String _REV = "_rev";		
		
	public class LogCache {
		public final static String CACHE = "cache";
		public final static String TIME_OFFSET = "timeOffset";
	}
		
	public class INotification {
		public class Type {
			public final static int NEW_KEY = 600;
			public static final int KEY_SENT = 601;
			public static final int EXPORTED_MEDIA = 602;
			public static final int SHARED_MEDIA = 603;
		}

		public static final String ID = "notification_id";
		public static final String CLASS = "handler_message_type";
	}
		
	public class IGenealogy {
		public class OwnershipType {
			public final static int INDIVIDUAL = 400;
			public final static int ORGANIZATION = 401;
		}
	}
		
	public class IRegion {
		public final static String REGION_BOUNDS = "region_bounds";
		public static final String REGION_COORDINATES = "region_coordinates";
		public static final String REGION_DIMENSIONS = "region_dimensions";
		public static final String REGION_TIMESTAMPS = "region_timestamps";
			
		public static final String DISPLAY_TOP = "displayTop";
		public static final String DISPLAY_LEFT = "displayLeft";
		public static final String DISPLAY_WIDTH = "displayWidth";
		public static final String DISPLAY_HEIGHT = "displayHeight";
		public static final String BOUNDS = "bounds";
		public static final String INDEX = "index";
		public static final String ID = "id";
			
		public class Bounds {
			public final static String TOP = "top";
			public final static String LEFT = "left";
			public final static String WIDTH = "width";
			public final static String HEIGHT = "height";
			public final static String START_TIME = "startTime";
			public final static String END_TIME = "endTime";
			public static final String DURATION = "duration";
		}
	}
		
	public class IUser {
		public final static String PATH_TO_BASE_IMAGE = "path_to_base_image";
		public final static String AUTH_TOKEN = "auth_token";
		public final static String PASSWORD = "password";
		public static final String ALIAS = "alias";
		public static final String EMAIL = "email";
		public static final String BASE_IMAGE = "baseImage";
		public static final String CREDENTIALS = "credentials";
		public static final String SECRET = "secret";
		public static final String SECRET_AUTH_TOKEN = "secretAuthToken";
		public static final String SECRET_KEY = "secretKey";
		public static final String PGP_KEY_FINGERPRINT = "pgpKeyFingerprint";
		public static final String PUBLIC_CREDENTIALS = "publicCredentials";
		public static final String PUBLIC_KEY = "publicKey";
		public static final String BELONGS_TO_USER = "belongs_to_user";
		public static final String LANG_DEFAULT = "langDefault";
	//	public static final String ASSET_ENCRYPTION = "assetEncryption";
	}

	public class IMediaManifest {
		public class Sort {
			public final static int DATE_DESC = 0;
			public final static int DATE_ASC = 3;
			public final static int TYPE_PHOTO = 1;
			public final static int TYPE_VIDEO = 2;
			public final static int SOURCE_IOCIPHER = 4;
			public final static int SOURCE_FILE_SYSTEM = 4;
				
			//public final static int LOCATION = 0;
			public final static String IS_SHOWING = "isShowing";
		}
	}
		
	public class INotificationManifest {
		public class Sort {
			public final static int DATE_DESC = IMediaManifest.Sort.DATE_DESC;
			public static final int COMPLETED = 2;
			public final static int DATE_ASC = IMediaManifest.Sort.DATE_ASC;
		}
	}

	public class IMedia {
		public final static String _ID = "_id";
		public static final String J3M = "j3m";
		public static final String J3M_DESCRIPTOR = "j3m_descriptor";
		public static final String VERSION = "versionForExport";
			
		public class Flags {
			public final static String IS_NEW = "isNew";
		}
			
		public class ILog {
			public final static String ATTACHED_MEDIA = "attachedMedia";
			public final static String IS_CLOSED = "isClosed";
			public final static String START_TIME = "startTime";
			public final static String END_TIME = "endTime";
		}
			
		public class Data {

			public static final String SENSOR_PLAYBACK = "sensorPlayback";
				
		}
			
		public class Image {
			public static final String BITMAP = "bitmap";
		}
			
		public class Video {
			public static final String VIDEO = "video";
		}

		public class MimeType {
			public final static String IMAGE = "image/jpeg";
			public final static String VIDEO_BASE = "video/";
			public final static String VIDEO_MP4 = "video/mp4";
			public final static String VIDEO_3GPP = "video/3gpp";
			public static final String LOG = "informacam/log";
			public static final String ZIP = "application/zip";
			public static final String ICTD = "application/octet-stream";
			public static final String JSON = "application/json";
			public static final String J3M = JSON;
		}
			
		public class Assets {
		//	public final static String J3M = "informacam.j3m";
		}

		public class j3m {
			public final static String DATA = "data";
			public final static String GENEALOGY = "genealogy";
			public final static String INTENT = "intent";
			public static final String SIGNATURE = "signature";
			public static final String SIZE = "size";
			public static final String HASH = "hash";
			public static final String FILE_NAME = "file_name";
			public static final String J3M = "j3m";
		}
			
		public class TempKeys {
			public final static String IS_SELECTED = "isBatchSelected";
			public final static String SHOULD_SHOW = "shouldShow";
		}
	}

	public class ICredentials {
		public final static String PASSWORD_BLOCK = "passwordBlock";
	}

	public class IPendingConnections {

	}

	public class IConnection {
			

		public static final String DATA = "data";
		public static final String PARAMS = "params";
		public static final String _ID = "_id";
		public static final String _REV = "_rev";
		public static final String BELONGS_TO_USER = "belongs_to_user";
		public static final String BYTE_RANGE = "byte_range";
			
		public static final int MAX_TRIES = 10;
		public static final String PATH_TO_NEXT_CONNECTION_DATA = "pathToNextConnectionData";
		public static final String BYTES_TRANSFERRED = "bytes_transferred";
		public static final String BYTES_TRANSFERRED_VERIFIED = "bytes_transferred_verified";
		public static final String PROGRESS = "progress";
		public static final String PARENT = "parent";
			
		public class ResponseCodes {
			public static final int INVALID_TICKET = 48;
		}
			
		public class Type {
			public static final int NONE = 799;
			public static final int MESSAGE = 800;
			public static final int SUBMISSION = 801;
			public static final int UPLOAD = 802;
		}
			
		public class CommonParams {
			public static final String MESSAGE_TO = "message_to";
			public static final String MESSAGE_TIME = "message_time";
			public static final String MESSAGE_CONTENT = "message_content";
		}
			
		public class Routes {
			public static final String EXPORT = "export/";
			public static final String MESSAGES = "messages/";
			public static final String SUBMISSIONS = "submissions/";
			public static final String UPLOAD = "upload/";
		}
	}

	public class IDCIMEntry {
		public final static String FILE_NAME = "fileName";
		public static final String SIZE = "size";
		public static final String URI = "uri";
		public static final String TIME_CAPTURED = "timeCaptured";
		public static final String HASH = "hash";
		public static final String THUMBNAIL = "thumbnail";
		public static final String AUTHORITY = "authority";
		public static final String MEDIA_TYPE = "mediaType";
		public static final String INDEX = "index";
	}

	public class IDCIMDescriptor {
		public static final String TAG = "IDCIMDescriptor";
	}

	public class IResult {
		public final static String DATA = "data";
		public final static String REASON = "reason";
		public static final String RESPONSE_CODE = "response_code";
		public static final String CONTENT = "content";
		public static final String RESULT_CODE = "result";
			
		public class ResponseCodes {
			public static final int DOWNLOAD_ASSET = 43;
			public final static int INIT_USER = 44;
			public static final int INSTALL_ICTD = 45;
			public static final int UPLOAD_SUBMISSION = 46;
			public static final int UPLOAD_CHUNK = 47;
		}
	}

	public class ITransportStub {
		public static final int MAX_TRIES = 6;	//10;
			
		public static final String ID = "id";
		public static final String ID_HASH = "8913k5zfpo16asb08ep821wery";
			
		public static final String ASSOCIATED_NOTIFICATION = "associatedNotification";
		public static final String ORGANIZATION = "organization";
		public static final String ASSET_PATH = "assetPath";
			
		public class Methods {
			public final static int GET = 1;
			public static final int POST = 2;
			public static final int PUT = 3;
				
		}
			
		public static final String TAG = "transport_stub";
			
		public class RepositorySources {
			public final static String GOOGLE_DRIVE = "google_drive";
			public final static String GLOBALEAKS = "globaleaks";
			public final static String APP = "application";
			public final static String S3 = "s3";
			public final static String CAMERAV_EXPRESS = "camerav_express";
		}
			
		public class ResultCodes {
			public final static int FAIL = 403;
			public final static int OK = 200;
		}
			
		public class CallbackCodes {
			public final static int UPDATE_ORGANIZATION_HAS_KEY = 100;
		}
			
		public class Globaleaks {
			public final static String TAG = RepositorySources.GLOBALEAKS;
		}
			
		public class S3 {
			public final static String TAG = RepositorySources.S3;
		}
			
		public class GoogleDrive {
			public final static String TAG = RepositorySources.GOOGLE_DRIVE;
			public static final String SCOPE = "oauth2:https://www.googleapis.com/auth/drive.file";
				
			public class Urls {
				public final static String UPLOAD = "https://www.googleapis.com/upload/drive/v2/files?uploadType=multipart";
				public final static String SHARE = "https://www.googleapis.com/drive/v2/files/%s/permissions";
					
			}
				
			public class Permissions {
				public final static String USER = "user";
			}
				
			public class Roles {
				public final static String WRITER = "writer";
			}
		}
	}

	public class IIdentity {
		public final static String SOURCE = "source";
		public static final String CREDENTIALS = "credentials";
	}

	public class IOrganization {
		public static final String ORGANIZATION_DETAILS = "organizationDetails";
		public static final String ORGANIZATION_ICON = "organizationIcon";
		public static final String ORGANIZATION_NAME = "organizationName";
		public static final String ORGANIZATION_FINGERPRINT = "organizationFingerprint";
		public static final String PUBLIC_KEY = "publicKey";
		public static final String FORMS = "forms";
		public static final String REPOSITORIES = "repositories";
		public static final String APPLICATION_SIGNATURE = "applicationSignature";
		public static final String PACKAGE_NAME = "packageName";
	}
} public class MimeType {
	public final static String IMAGE = "image/jpeg";
	public final static String VIDEO_BASE = "video/";
	public final static String VIDEO_MP4 = "video/mp4";
	public final static String VIDEO_3GPP = "video/3gpp";
	public static final String LOG = "informacam/log";
	public static final String ZIP = "application/zip";
	public static final String ICTD = "application/octet-stream";
	public static final String JSON = "application/json";
	public static final String J3M = JSON;
} public final static class Suckers {
	public final static String LOG = "InformaSuckers";
	public static final int GPS_WAIT_MAX = 30;
	public static final int DEFAULT_CRON_INTERVAL = 45;
	public static final int DEFAULT_CRON_ACTIVE_INTERVAL = 1;

	public final static class CaptureEvent {
		public final static int METADATA_CAPTURED = 272;
		public final static int MEDIA_OPENED = 273;
		public final static int REGION_GENERATED = 274;
		public final static int MEDIA_SAVED = 275;
		public final static int SENSOR_PLAYBACK = 271;
		public final static int TIMESTAMPS_RESOLVED= 270;
		public final static int FORM_EDITED = 269;
			
			
		public final static class Keys {
			public final static String USER_ACTION = "userActionReported";
			public final static String TYPE = "captureTypes";
			public final static String MATCH_TIMESTAMP = "captureEventMatchTimestamp";
			public final static String TIMESTAMP = "captureEventTimestamp";
			public final static String ON_VIDEO_START = "timestampOnVideoStart";
			public final static String MEDIA_CAPTURE_COMPLETE = "mediaCapturedComplete";
			public final static String METADATA_CAPTURED = "metadataCaptured";
			public final static String REGION_LOCATION_DATA = "regionLocationData";
		}
	}

	public final static class Device {
           public final static long LOG_RATE = 10000L;

           public final static class Keys {
               public static final String FILE_EFFECTED = "fileEffected";
               public static final String ACCESS_TYPE = "accessType";
               public static final String ACCESS_CODE = "accessCode";
               public static final String PLUG_EVENT_TYPE = "plugEventType";
               public static final String PLUG_EVENT_CODE = "plugEventCode";
           }
       }

	public final static class Phone {
		public final static long LOG_RATE = 20000L;

		public final static class Keys {
			public static final String CELL_ID = "cellTowerId";
			public static final String BLUETOOTH_DEVICE_ADDRESS = "bluetoothDeviceAddress";
			public static final String BLUETOOTH_DEVICE_NAME = "bluetoothDeviceName";
			public static final String VISIBLE_WIFI_NETWORKS = "visibleWifiNetworks";
			public static final String BSSID = "bssid";
			public static final String SSID = "ssid";
			public static final String WIFI_FREQ = "wifiFreq";
			public static final String WIFI_LEVEL = "wifiLevel";
			public static final String BLUETOOTH_DEVICE_REDACTED = "bluetoothDeviceRedacted";
			public static final String LAC = "LAC";
			public static final String MCC = "MCC";
			public static final String MNC = "MNC";
		}
	}

	public final static class Accelerometer {
		public final static long LOG_RATE = 2000L; //let's lower this for now
			
		public final static class Keys {
			public static final String ACC = "acc";
			public static final String ORIENTATION = "orientation";
			public static final String X = "acc_x";
			public static final String Y = "acc_y";
			public static final String Z = "acc_z";
			public static final String PITCH = "pitch";
			public static final String ROLL = "roll";
			public static final String AZIMUTH = "azimuth";
			public static final String PITCH_CORRECTED = "pitchCorrected";
			public static final String ROLL_CORRECTED = "rollCorrected";
			public static final String AZIMUTH_CORRECTED = "azimuthCorrected";
			public static final String BEARING_DEGREES = "bearingDegrees";
		}
	}
		
	public final static class Environment {
		public final static long LOG_RATE = 1000L;
			
		public final static class Keys {
			public static final String AMBIENT_TEMP = "ambientTemperature";
			public static final String AMBIENT_TEMP_CELSIUS = "ambientTemperatureCelsius";
				
			public static final String DEVICE_TEMP = "deviceTemperature";
			public static final String DEVICE_TEMP_CELSIUS = "deviceTemperatureCelsius";
				
			public static final String HUMIDITY = "relativeHumidity";
			public static final String HUMIDITY_PERC = "relativeHumidityPercentile";
				
			public static final String PRESSURE = "pressure";
			public static final String PRESSURE_MBAR = "pressureHPAOrMBAR";
			public static final String PRESSURE_ALTITUDE = "pressureAltitude";

			public static final String LIGHT = "light";
			public static final String LIGHT_METER_VALUE = "lightMeterValue";
		}
	}

	public final static class Geo {
		public final static long LOG_RATE = 5000L;

		public final static class Keys {
			public static final String GPS_COORDS = "gps_coords";
			public static final String GPS_BEARING = "gps_bearing";
			public static final String GPS_ALTITUDE = "gps_altitude";
			public static final String GPS_SPEED = "gps_speed";
			public static final String GPS_ACCURACY = "gps_accuracy";
			public static final String GPS_TIME = "gps_time";
				
			public static final String NMEA_TIME = "nmeatime";
			public static final String NMEA_MESSAGE = "nmeamessage";
				
		}
	}
}
import java.io.BufferedInputStream;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.io.StringBufferInputStream;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Enumeration;
import java.util.List;
import java.util.Locale;
import java.util.Vector;
import org.apache.commons.io.IOUtils;
import org.spongycastle.openpgp.PGPException;
import org.witness.informacam.crypto.CredentialManager;
import org.witness.informacam.crypto.KeyUtility;
import org.witness.informacam.crypto.SignatureService;
import org.witness.informacam.informa.Cron;
import org.witness.informacam.informa.InformaService;
import org.witness.informacam.json.JSONArray;
import org.witness.informacam.json.JSONException;
import org.witness.informacam.json.JSONObject;
import org.witness.informacam.json.JSONTokener;
import org.witness.informacam.models.Model;
import org.witness.informacam.models.credentials.IKeyStore;
import org.witness.informacam.models.credentials.ISecretKey;
import org.witness.informacam.models.credentials.IUser;
import org.witness.informacam.models.forms.IForm;
import org.witness.informacam.models.forms.IInstalledForms;
import org.witness.informacam.models.j3m.IDCIMDescriptor;
import org.witness.informacam.models.media.IMedia;
import org.witness.informacam.models.media.IMediaManifest;
import org.witness.informacam.models.notifications.INotification;
import org.witness.informacam.models.notifications.INotificationsManifest;
import org.witness.informacam.models.organizations.IInstalledOrganizations;
import org.witness.informacam.models.organizations.IOrganization;
import org.witness.informacam.models.transport.ITransportManifest;
import org.witness.informacam.models.transport.ITransportStub;
import org.witness.informacam.storage.FormUtility;
import org.witness.informacam.storage.IOService;
import org.witness.informacam.storage.IOUtility;
import org.witness.informacam.transport.TransportUtility;
import org.witness.informacam.utils.Constants.Actions;
import org.witness.informacam.utils.Constants.App;
import org.witness.informacam.utils.Constants.App.Informa;
import org.witness.informacam.utils.Constants.App.Storage;
import org.witness.informacam.utils.Constants.App.Storage.Type;
import org.witness.informacam.utils.Constants.Codes;
import org.witness.informacam.utils.Constants.IManifest;
import org.witness.informacam.utils.Constants.InformaCamEventListener;
import org.witness.informacam.utils.Constants.ListAdapterListener;
import org.witness.informacam.utils.Constants.Logger;
import org.witness.informacam.utils.Constants.Models;
import org.witness.informacam.utils.Constants.Models.IMedia.MimeType;
import org.witness.informacam.utils.Constants.Suckers;
import org.witness.informacam.utils.InformaCamBroadcaster.InformaCamStatusListener;
import org.witness.informacam.utils.InnerBroadcaster;
import android.app.Activity;
import android.app.AlarmManager;
import android.app.Application;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.res.Configuration;
import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.preference.PreferenceManager;
import android.support.multidex.MultiDex;
import android.support.multidex.MultiDexApplication;
import android.util.Base64;
import android.util.Log;
import dalvik.system.DexFile;
package org.witness.informacam;




public class InformaCam extends MultiDexApplication {
	/*
	 * set dynamic debug settings in initData
	 */
	
	private final static String LOG = App.LOG;

	private List<InnerBroadcaster> broadcasters = new Vector<InnerBroadcaster>();

	public IMediaManifest mediaManifest = new IMediaManifest();
	public IInstalledOrganizations installedOrganizations = new IInstalledOrganizations();
	public INotificationsManifest notificationsManifest = new INotificationsManifest();
	public ITransportManifest transportManifest = new ITransportManifest();
	
	public IUser user;

	Intent ioServiceIntent, signatureServiceIntent, uploaderServiceIntent, informaServiceIntent;

	public IOService ioService = null;
	public SignatureService signatureService = null;
	public Handler h = new Handler();

	public List<String> models = new ArrayList<String>();

	NotificationManager notificationManager;
	
	private int processId;

	private static InformaCam mInstance = null;
	
	private ListAdapterListener mListAdapterListener = null;
	private InformaCamStatusListener mStatusListener = null;
	private InformaCamEventListener mEventListener = null;
	
	private CredentialManager credentialManager = null;
	private PendingIntent cronPI = null;
	
	
	//we really need to get rid of this
	public static InformaCam getInstance ()
	{
		return mInstance;
	}
	
	public void initBroadcasters() {
		for(BroadcastReceiver br : broadcasters) {
			if(!((InnerBroadcaster) br).isMounted()) {
				((InnerBroadcaster) br).setMounted(true);
				registerReceiver(br, ((InnerBroadcaster) br).intentFilter);
			}
		}
	}

    @Override
    protected void attachBaseContext(Context base) {
        super.attachBaseContext(base);
        MultiDex.install(this);
    }

	@Override
	public void onCreate() {
		super.onCreate();
		
		mInstance = this;
		processId = android.os.Process.myPid();

		updateLocale ();

		Logger.d(LOG, "InformaCam service started via intent");
		
		notificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);

		broadcasters.add(new InnerBroadcaster(new IntentFilter(Actions.ASSOCIATE_SERVICE), processId) {