package eu.musesproject.client.contextmonitoring.sensors;
/*
* #%L
* musesclient
* %%
* Copyright (C) 2013 - 2014 HITEC
* %%
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* #L%
*/
import android.os.Environment;
import android.os.FileObserver;
import android.util.Log;
import eu.musesproject.client.contextmonitoring.ContextListener;
import eu.musesproject.client.db.entity.SensorConfiguration;
import eu.musesproject.contextmodel.ContextEvent;
import java.io.File;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* @author christophstanik
*
* Class to collect information about accesses and modifications
* to a specific directory or file in a given directory on the device
*/
public class RecursiveFileSensor implements ISensor {
private static final String TAG = RecursiveFileSensor.class.getSimpleName();
// sensor identifier
public static final String TYPE = "CONTEXT_SENSOR_FILEOBSERVER";
// context property keys
public static final String PROPERTY_KEY_FILE_EVENT = "fileevent";
public static final String PROPERTY_KEY_PATH = "path";
public static final String PROPERTY_KEY_NAME = "resourceName";
// config keys
public static final String CONFIG_KEY_PATH = "path";
// possible events
public static final String OPEN = "open";
public static final String ATTRIB = "metadata";
public static final String ACCESS = "access";
public static final String CREATE = "create";
public static final String DELETE = "delete";
public static final String MODIFY = "modify";
public static final String MOVED_FROM = "moved_from";
public static final String MOVED_TO = "moved_to";
public static final String MOVE_SELF = "move_self";
public static final String CLOSE_WRITE = "close_write";
public static final String CLOSE_NOWRITE = "close_no_write";
private Map<String, String> paths;
private List<FileSensor> observers;
private FileSensor fileObserver;
private String fullPath;
private String rootPathToObserve;
private ContextListener listener;
// history of fired context events
List<ContextEvent> contextEventHistory;
// file observation already started
private boolean running;
public RecursiveFileSensor() {
init();
}
// initializes all necessary default values
private void init() {
running = false;
// example directory.
// rootPathToObserve = "/Pictures/";
contextEventHistory = new ArrayList<ContextEvent>(CONTEXT_EVENT_HISTORY_SIZE);
paths = new HashMap<String, String>();
}
/**
* This method enables the sensor and initiates the context event collection.
* If the implemented File Observer fires an event it will be created in
* {@link RecursiveFileSensor#createContextEvent(String, String, String)}
*/
@Override
public void enable() {
if(!running){
Log.d(TAG, "start file observation");
startObservation();
}
}
private void startObservation() {
observers = new ArrayList<FileSensor>();
fullPath = Environment.getExternalStorageDirectory().getAbsolutePath() + rootPathToObserve;
findDirectoriesToObserve(new File(fullPath));
if(paths != null && paths.size() > 0) {
for (String path : paths.keySet()) {
Log.d(TAG, "path to observe: " + path);
observers.add(new FileSensor(path));
}
running = true;
}
}
private void findDirectoriesToObserve(File file) {
if (file.isDirectory()) {
Log.d(TAG,"Searching directory ... " + file.getAbsoluteFile());
paths.put(file.getAbsolutePath(), file.getAbsolutePath());
//do you have permission to read this directory?
if (file.canRead() && (file.listFiles() != null)) {
for (File temp : file.listFiles()) {
if (temp.isDirectory()) {
paths.put(temp.getAbsolutePath(), temp.getAbsolutePath());
findDirectoriesToObserve(temp);
}
}
} else {
Log.d(TAG ,file.getAbsoluteFile() + "Permission Denied");
}
}
}
/**
* create the context event for this sensor
* @param eventText private static field of {@link RecursiveFileSensor} that describes the event.
* @param path related to the file that fires the event
* @param name
*/
public void createContextEvent(String eventText, String path, String name) {
ContextEvent contextEvent = new ContextEvent();
contextEvent.setType(TYPE);
contextEvent.setTimestamp(System.currentTimeMillis());
contextEvent.addProperty(PROPERTY_KEY_FILE_EVENT, eventText);
contextEvent.addProperty(PROPERTY_KEY_PATH, path);
contextEvent.addProperty(PROPERTY_KEY_NAME, name);
contextEvent.generateId();
Log.d(TAG, "event received: " + eventText + " path: " +path + " name: " + name);
// add context event to the context event history
contextEventHistory.add(contextEvent);
if(contextEventHistory.size() > CONTEXT_EVENT_HISTORY_SIZE) {
contextEventHistory.remove(0);
}
if(listener != null) {
listener.onEvent(contextEvent);
}
}
@Override
public void disable() {
Log.d(TAG, "stop file observation...waiting for confirmation");
if(running) {
try {
if(observers!= null && observers.size() > 0) {
for (FileSensor fileSensor : observers) {
fileSensor.stopWatching();
}
}
fileObserver.stopWatching();
running = false;
Log.d(TAG, "confirmation: file observation stopped");
} catch (Exception e) {
Log.e(TAG, e.getMessage());
}
}
}
@Override
public void addContextListener(ContextListener listener) {
this.listener = listener;
}
@Override
public void removeContextListener(ContextListener listener) {
this.listener = listener;
}
@Override
public ContextEvent getLastFiredContextEvent() {
if(contextEventHistory.size() > 0) {
return contextEventHistory.get(contextEventHistory.size() - 1);
}
else {
return null;
}
}
public String getPathToObserve() {
return rootPathToObserve;
}
public void setPathToObserve(String rootPathToObserve) {
this.rootPathToObserve = rootPathToObserve;
}
@Override
public void configure(List<SensorConfiguration> config) {
for (SensorConfiguration item : config) {
if(item.getKey().equals(CONFIG_KEY_PATH)) {
rootPathToObserve = item.getValue();
Log.d(TAG, item.getValue());
}
}
}
@Override
public String getSensorType() {
return TYPE;
}
public class FileSensor extends FileObserver {
// these variables are needed to prevent the context event creation multiple times a second.
// this is necessary because the FileObserver fires the same event multiple times
int oldEvent = - 1;
long lastEventTimestamp = System.currentTimeMillis();
long threshold = 1000;
String rootPath;
public FileSensor(String path) {
super(path);
this.rootPath = path;
startWatching();
}
@Override
public void onEvent(int event, String name) {
long eventTimeStamp = System.currentTimeMillis();
// add ALL_EVENTS to erase high bit values
event &= ALL_EVENTS;
String eventText = null;
if((oldEvent != event) || ((eventTimeStamp - lastEventTimestamp) >= threshold)) {
oldEvent = event;
lastEventTimestamp = eventTimeStamp;
switch(event){
case FileObserver.OPEN : eventText = RecursiveFileSensor.OPEN; break;
case FileObserver.ATTRIB : eventText = RecursiveFileSensor.ATTRIB; break;
case FileObserver.ACCESS : eventText = RecursiveFileSensor.ACCESS; break;
case FileObserver.CREATE : eventText = RecursiveFileSensor.CREATE; break;
case FileObserver.DELETE : eventText = RecursiveFileSensor.DELETE; break;
case FileObserver.MODIFY : eventText = RecursiveFileSensor.MODIFY; break;
case FileObserver.MOVED_FROM : eventText = RecursiveFileSensor.MOVED_FROM; break;
case FileObserver.MOVED_TO : eventText = RecursiveFileSensor.MOVED_TO; break;
case FileObserver.MOVE_SELF : eventText = RecursiveFileSensor.MOVE_SELF; break;
case FileObserver.CLOSE_WRITE : eventText = RecursiveFileSensor.CLOSE_WRITE; break;
case FileObserver.CLOSE_NOWRITE: eventText = RecursiveFileSensor.CLOSE_NOWRITE;break;
default: break;
}
if((eventText != null) && (name != null)) {
String path = rootPath + "/" + name;
File file = new File(path);
if(file.isFile()) {
createContextEvent(eventText, rootPath, name);
}
}
}
}
}
}