Moved system logging to use sqlite database rather than file - removed need for access external strage permission.

This commit is contained in:
Graham Jones
2022-02-22 22:21:36 +00:00
parent 256536fa4f
commit 7148121a36
9 changed files with 450 additions and 218 deletions

View File

@@ -68,17 +68,15 @@ import java.util.function.Consumer;
public class LogManager {
static final private String TAG = "LogManager";
//private String mDbName = "osdData";
final private String mDpTableName = "datapoints";
final private String mSysLogTableName = "syslog";
final static private String mDpTableName = "datapoints";
private boolean mLogRemote;
private boolean mLogRemoteMobile;
private String mAuthToken;
static private OsdDbHelper mDpDb; // Datapoints table helper.
private OsdDbHelper mSysLogDb; // Datapoints table helper.
static private SQLiteDatabase mOsdDb = null; // SQLite Database for data and log entries.
private RemoteLogTimer mRemoteLogTimer;
private Context mContext;
private static Context mContext;
private OsdUtil mUtil;
public WebApiConnection mWac;
public static WebApiConnection mWac;
private boolean mUploadInProgress;
private long mEventDuration = 120; // event duration in seconds - uploads datapoints that cover this time range centred on the event time.
@@ -116,16 +114,22 @@ public class LogManager {
mUtil = new OsdUtil(mContext, handler);
openDb();
Log.i(TAG,"Starting Remote Database Interface");
mWac = new WebApiConnection(mContext);
mWac.setStoredToken(mAuthToken);
if (mLogRemote) {
Log.i(TAG,"Starting Remote Log Timer");
startRemoteLogTimer();
} else {
Log.i(TAG,"mLogRemote is false - not starting remote log timer");
}
if (mAutoPruneDb) {
Log.v(TAG, "Starting Auto Prune Timer");
Log.i(TAG, "Starting Auto Prune Timer");
startAutoPruneTimer();
} else {
Log.v(TAG, "AutoPruneDB is not set");
Log.i(TAG, "AutoPruneDB is not set - not starting Auto Prune Timer");
}
}
@@ -168,43 +172,34 @@ public class LogManager {
}
private boolean openDb() {
private static boolean openDb() {
Log.d(TAG, "openDb");
try {
mDpDb = new OsdDbHelper(mDpTableName, mContext);
if (!checkTableExists(mDpDb, mDpTableName)) {
Log.e(TAG, "ERROR - Table does not exist");
if (mOsdDb == null) {
Log.i(TAG,"openDb: mOsdDb is null - initialising");
mOsdDb = new OsdDbHelper(mContext).getWritableDatabase();
} else {
Log.i(TAG,"openDb: mOsdDb has been initialised already so not doing anything");
}
if (!checkTableExists(mOsdDb, mDpTableName)) {
Log.e(TAG, "ERROR - Table "+mDpTableName+" does not exist");
return false;
} else {
Log.d(TAG, "table " + mDpTableName + " exists ok");
}
} catch (SQLException e) {
Log.e(TAG, "Failed to open Database: " + e.toString());
mDpDb = null;
return false;
}
try {
mSysLogDb = new OsdDbHelper(mSysLogTableName, mContext);
if (!checkTableExists(mSysLogDb, mSysLogTableName)) {
Log.e(TAG, "ERROR - SysLog table does not exist");
return false;
} else {
Log.d(TAG, "table " + mSysLogTableName + " exists ok");
}
return true;
} catch (SQLException e) {
Log.e(TAG, "Failed to open Syslog Database: " + e.toString());
mSysLogDb = null;
return false;
}
}
private boolean checkTableExists(OsdDbHelper osdDb, String osdTableName) {
private static boolean checkTableExists(SQLiteDatabase osdDb, String osdTableName) {
Cursor c = null;
boolean tableExists = false;
Log.d(TAG, "checkTableExists()");
try {
c = osdDb.getWritableDatabase().query(osdTableName, null,
c = osdDb.query(osdTableName, null,
null, null, null, null, null);
tableExists = true;
c.close();
@@ -239,7 +234,7 @@ public class LogManager {
+ DatabaseUtils.sqlEscapeString(sdData.toJSON(true)) + ","
+ 0
+ ")";
mDpDb.getWritableDatabase().execSQL(SQLStr);
mOsdDb.execSQL(SQLStr);
Log.v(TAG, "data written to database");
} catch (SQLException e) {
@@ -249,37 +244,6 @@ public class LogManager {
}
/**
* Write syslog string to local database
* FIXME - I am sure we should not be using raw SQL Srings to do this!
*/
public void writeLogEntryToLocalDb(String logText, int statusVal) {
Log.v(TAG, "writeLogEntryToLocalDb()");
Date curDate = new Date();
SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
String dateStr = dateFormat.format(curDate);
String SQLStr = "SQLStr";
try {
SQLStr = "INSERT INTO " + mSysLogTableName
+ "(dataTime, status, dataJSON, uploaded)"
+ " VALUES("
+ "'" + dateStr + "',"
+ statusVal + ","
+ DatabaseUtils.sqlEscapeString(logText) + ","
+ 0
+ ")";
mSysLogDb.getWritableDatabase().execSQL(SQLStr);
Log.v(TAG, "syslog entry written to database: "+logText);
} catch (SQLException e) {
Log.e(TAG, "writeToLocalDb(): Error Writing Data: " + e.toString());
Log.e(TAG, "SQLStr was " + SQLStr);
}
}
/**
* Returns a json representation of datapoint 'id'.
@@ -296,7 +260,7 @@ public class LogManager {
//String[] selectArgs = new String[]{String.format("%d", id)};
//c = mOSDDb.getWritableDatabase().query(mDbTableName, null,
// selectStr, selectArgs, null, null, null);
c = mDpDb.getWritableDatabase().rawQuery(selectStr, null);
c = mOsdDb.rawQuery(selectStr, null);
retVal = cursor2Json(c);
} catch (Exception e) {
Log.d(TAG, "getDatapointById(): Error Querying Database: " + e.getLocalizedMessage());
@@ -317,7 +281,7 @@ public class LogManager {
Log.d(TAG, "setDatapointToUploaded() - id=" + id);
ContentValues cv = new ContentValues();
cv.put("uploaded", eventId);
int nRowsUpdated = mDpDb.getWritableDatabase().update(mDpTableName, cv, "id = ?",
int nRowsUpdated = mOsdDb.update(mDpTableName, cv, "id = ?",
new String[]{String.format("%d", id)});
return (nRowsUpdated == 1);
@@ -336,7 +300,7 @@ public class LogManager {
//Cursor c = null;
ContentValues cv = new ContentValues();
cv.put("status", statusVal);
int nRowsUpdated = mDpDb.getWritableDatabase().update(mDpTableName, cv, "id = ?",
int nRowsUpdated = mOsdDb.update(mDpTableName, cv, "id = ?",
new String[]{String.format("%d", id)});
return (nRowsUpdated == 1);
@@ -419,7 +383,7 @@ public class LogManager {
try {
String selectStr = "DataTime<=?";
String[] selectArgs = {endDateStr};
retVal = mDpDb.getWritableDatabase().delete(mDpTableName, selectStr, selectArgs);
retVal = mOsdDb.delete(mDpTableName, selectStr, selectArgs);
} catch (Exception e) {
Log.d(TAG, "Error deleting datapoints" + e.toString());
retVal = 0;
@@ -599,7 +563,7 @@ public class LogManager {
+ ", mHaving =" + mHaving + ", mOrderBy=" + mOrderBy);
try {
Cursor resultSet = mDpDb.getWritableDatabase().query(mTable, mColumns, mSelection,
Cursor resultSet = mOsdDb.query(mTable, mColumns, mSelection,
mSelectionArgs, mGroupBy, mHaving, mOrderBy);
resultSet.moveToFirst();
return (resultSet);
@@ -826,26 +790,36 @@ public class LogManager {
/**
* close() - shut down the logging system
* WARNING - this should only be called by the final destructor of the app (not individual class destructors)
* because it will close the DB for all instances of LogManger, not just the one on which it is called.
* FIXME: If I was keen I would keep a count of how many instances of LogManager there are, and have this function do nothing
* unless it was the last instance.
*/
public void close() {
mDpDb.close();
stopRemoteLogTimer();
public static void close() {
mOsdDb.close();
mOsdDb = null;
if (mWac != null) {
Log.i(TAG,"Stopping Remote Database");
Log.i(TAG,"Stopping Remote Database Interface");
mWac.close();
}
}
public void stop() {
// Stop the timers and shutdown the remote API connection.
stopRemoteLogTimer();
stopAutoPruneTimer();
}
/*
* Start the timer that will upload data to the remote server after a given period.
*/
private void startRemoteLogTimer() {
if (mRemoteLogTimer != null) {
Log.v(TAG, "startRemoteLogTimer -timer already running - cancelling it");
Log.i(TAG, "startRemoteLogTimer -timer already running - cancelling it");
mRemoteLogTimer.cancel();
mRemoteLogTimer = null;
}
Log.v(TAG, "startRemoteLogTimer() - starting RemoteLogTimer");
Log.i(TAG, "startRemoteLogTimer() - starting RemoteLogTimer");
mRemoteLogTimer =
new RemoteLogTimer(mRemoteLogPeriod * 1000, 1000);
mRemoteLogTimer.start();
@@ -857,7 +831,7 @@ public class LogManager {
*/
public void stopRemoteLogTimer() {
if (mRemoteLogTimer != null) {
Log.v(TAG, "stopRemoteLogTimer(): cancelling Remote Log timer");
Log.i(TAG, "stopRemoteLogTimer(): cancelling Remote Log timer");
mRemoteLogTimer.cancel();
mRemoteLogTimer = null;
}
@@ -869,11 +843,11 @@ public class LogManager {
*/
private void startAutoPruneTimer() {
if (mAutoPruneTimer != null) {
Log.v(TAG, "startAutoPruneTimer -timer already running - cancelling it");
Log.i(TAG, "startAutoPruneTimer -timer already running - cancelling it");
mAutoPruneTimer.cancel();
mAutoPruneTimer = null;
}
Log.v(TAG, "startAutoPruneTimer() - starting AutoPruneTimer");
Log.i(TAG, "startAutoPruneTimer() - starting AutoPruneTimer");
mAutoPruneTimer =
new AutoPruneTimer(mAutoPrunePeriod * 1000, 1000);
mAutoPruneTimer.start();
@@ -885,47 +859,46 @@ public class LogManager {
*/
public void stopAutoPruneTimer() {
if (mAutoPruneTimer != null) {
Log.v(TAG, "stopAutoPruneTimer(): cancelling Auto Prune timer");
Log.i(TAG, "stopAutoPruneTimer(): cancelling Auto Prune timer");
mAutoPruneTimer.cancel();
mAutoPruneTimer = null;
}
}
static public class OsdDbHelper extends SQLiteOpenHelper {
public static class OsdDbHelper extends SQLiteOpenHelper {
// If you change the database schema, you must increment the database version.
public static final int DATABASE_VERSION = 1;
public static final String DATABASE_NAME = "OsdData.db";
private final String mOsdTableName;
private static final String TAG = "LogManager.OsdDbHelper";
public OsdDbHelper(String osdTableName, Context context) {
public OsdDbHelper(Context context) {
super(context, DATABASE_NAME, null, DATABASE_VERSION);
Log.d(TAG, "OsdDbHelper constructor");
mOsdTableName = osdTableName;
}
public void onCreate(SQLiteDatabase db) {
Log.v(TAG, "onCreate - TableName=" + mOsdTableName);
String SQLStr = "CREATE TABLE IF NOT EXISTS " + mOsdTableName + "("
Log.i(TAG, "onCreate - TableName=" + mDpTableName);
String SQLStr = "CREATE TABLE IF NOT EXISTS " + mDpTableName + "("
+ "id INTEGER PRIMARY KEY,"
+ "dataTime DATETIME,"
+ "Status INT,"
+ "dataJSON TEXT,"
+ "uploaded INT"
+ ");";
db.execSQL(SQLStr);
}
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
// This database is only a cache for online data, so its upgrade policy is
// to simply to discard the data and start over
db.execSQL("Drop table if exists " + mOsdTableName + ";");
Log.i(TAG,"onUpgrade()");
db.execSQL("Drop table if exists " + mDpTableName + ";");
onCreate(db);
}
public void onDowngrade(SQLiteDatabase db, int oldVersion, int newVersion) {
Log.i(TAG,"onDowngrade()");
onUpgrade(db, oldVersion, newVersion);
}
}
@@ -947,7 +920,7 @@ public class LogManager {
@Override
public void onFinish() {
Log.v(TAG, "mRemoteLogTimer - onFinish - uploading data to remote database");
Log.d(TAG, "mRemoteLogTimer - onFinish - uploading data to remote database");
writeToRemoteServer();
// Restart this timer.
start();
@@ -969,7 +942,7 @@ public class LogManager {
@Override
public void onFinish() {
Log.v(TAG, "mAutoPruneTimer - onFinish - Pruning Local Database");
Log.d(TAG, "mAutoPruneTimer - onFinish - Pruning Local Database");
pruneLocalDb();
// Restart this timer.
start();

View File

@@ -51,16 +51,16 @@ public class LogManagerControlActivity extends AppCompatActivity {
private UiTimer mUiTimer;
private ArrayList<HashMap<String, String>> mEventsList;
private ArrayList<HashMap<String, String>> mRemoteEventsList;
private ArrayList<HashMap<String, String>> mSysLogList;
private SdServiceConnection mConnection;
private OsdUtil mUtil;
final Handler serverStatusHandler = new Handler();
private Integer mUiTimerPeriodFast = 2000; // 2 seconds - we use fast updating while UI is blank and we are waiting for first data
private Integer mUiTimerPeriodSlow = 60000; // 60 seconds - once data has been received and UI populated we only update once per minute.
private Integer UI_MODE_LOCAL = 0;
private Integer UI_MODE_SHARED = 1;
private Integer mUiMode = UI_MODE_SHARED;
private boolean mUpdateSysLog = true;
//private Integer UI_MODE_LOCAL = 0;
//private Integer UI_MODE_SHARED = 1;
//private Integer mUiMode = UI_MODE_SHARED;
@Override
protected void onCreate(Bundle savedInstanceState) {
@@ -198,7 +198,11 @@ public class LogManagerControlActivity extends AppCompatActivity {
Log.v(TAG, "initialiseServiceConnection() - set mEventsList - Updating UI");
updateUi();
});
//mEventsList = mLm.getEventsList(true);
mUtil.getSysLogList((ArrayList<HashMap<String, String>> syslogList) -> {
mSysLogList = syslogList;
Log.v(TAG, "initialiseServiceConnection() - set mSysLogList - Updating UI");
updateUi();
});
}
@@ -275,6 +279,16 @@ public class LogManagerControlActivity extends AppCompatActivity {
} else {
stopUpdating = false;
}
// SysLog ListView
if (mSysLogList != null && mUpdateSysLog) {
ListView lv = (ListView) findViewById(R.id.sysLogListView);
ListAdapter adapter = new SimpleAdapter(LogManagerControlActivity.this, mSysLogList, R.layout.syslog_entry_layout,
new String[]{"dataTime", "logLevel", "dataJSON"},
new int[]{R.id.syslog_entry_date_tv, R.id.syslog_level_tv, R.id.syslog_entry_text_tv});
lv.setAdapter(adapter);
//Log.v(TAG,"eventsList="+mEventsList);
mUpdateSysLog = false;
}
// Remote Database List View
if (mRemoteEventsList != null) {
ListView lv = (ListView) findViewById(R.id.remoteEventsLv);
@@ -316,8 +330,9 @@ public class LogManagerControlActivity extends AppCompatActivity {
} //updateUi();
public void onRadioButtonClicked(View view) {
LinearLayout localDataLl = (LinearLayout) findViewById(R.id.local_data_ll);;
LinearLayout sharedDataLl = (LinearLayout) findViewById(R.id.shared_data_ll);;;
LinearLayout localDataLl = (LinearLayout) findViewById(R.id.local_data_ll);
LinearLayout sharedDataLl = (LinearLayout) findViewById(R.id.shared_data_ll);
LinearLayout syslogLl = (LinearLayout) findViewById(R.id.syslog_ll);
// Is the button now checked?
boolean checked = ((RadioButton) view).isChecked();
@@ -328,6 +343,7 @@ public class LogManagerControlActivity extends AppCompatActivity {
// Switch to the local data view
localDataLl.setVisibility(View.VISIBLE);
sharedDataLl.setVisibility(View.GONE);
syslogLl.setVisibility(View.GONE);
}
break;
case R.id.shared_data_rb:
@@ -335,6 +351,15 @@ public class LogManagerControlActivity extends AppCompatActivity {
// Switch to the local data view
localDataLl.setVisibility(View.GONE);
sharedDataLl.setVisibility(View.VISIBLE);
syslogLl.setVisibility(View.GONE);
}
break;
case R.id.syslog_rb:
if (checked) {
// Switch to the local data view
localDataLl.setVisibility(View.GONE);
sharedDataLl.setVisibility(View.GONE);
syslogLl.setVisibility(View.VISIBLE);
}
break;
}

View File

@@ -306,7 +306,7 @@ public class MainActivity extends AppCompatActivity {
}
return true;
*/
case R.id.action_logs:
/* case R.id.action_logs:
Log.i(TAG, "action_logs");
try {
String url = "http://"
@@ -323,6 +323,7 @@ public class MainActivity extends AppCompatActivity {
Log.i(TAG, "exception starting log manager activity " + ex.toString());
}
return true;
*/
case R.id.action_logmanager:
Log.i(TAG, "action_logmanager");
try {

View File

@@ -27,60 +27,43 @@ package uk.org.openseizuredetector;
import android.Manifest;
import android.app.Activity;
import android.app.ActivityManager;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.ServiceConnection;
import android.content.SharedPreferences;
import android.content.pm.ActivityInfo;
import android.content.pm.ApplicationInfo;
import android.content.pm.FeatureInfo;
import android.content.pm.InstrumentationInfo;
import android.content.pm.PackageInfo;
import android.content.pm.PackageInstaller;
import android.content.pm.PackageManager;
import android.content.pm.PermissionGroupInfo;
import android.content.pm.PermissionInfo;
import android.content.pm.ProviderInfo;
import android.content.pm.ResolveInfo;
import android.content.pm.ServiceInfo;
import android.content.res.Resources;
import android.content.res.XmlResourceParser;
import android.graphics.Rect;
import android.graphics.drawable.Drawable;
import android.database.Cursor;
import android.database.DatabaseUtils;
import android.database.SQLException;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;
import android.net.ConnectivityManager;
import android.net.NetworkInfo;
import android.net.Uri;
import android.os.AsyncTask;
import android.os.Build;
import android.os.Environment;
import android.os.Handler;
import android.os.IBinder;
import android.os.UserHandle;
import android.preference.PreferenceManager;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.v4.app.ActivityCompat;
import android.support.v4.content.ContextCompat;
import android.text.format.Time;
import android.util.Log;
import android.view.MenuItem;
import android.widget.Toast;
import org.apache.http.conn.util.InetAddressUtils;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.net.InetAddress;
import java.net.NetworkInterface;
import java.util.AbstractList;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Arrays;
import java.util.Date;
import java.util.Enumeration;
import java.util.List;
import java.util.RandomAccess;
import java.util.concurrent.RunnableFuture;
import java.util.HashMap;
import java.util.function.Consumer;
/**
* OsdUtil - OpenSeizureDetector Utilities
@@ -92,7 +75,7 @@ public class OsdUtil implements ActivityCompat.OnRequestPermissionsResultCallbac
private final String DATALOG = "DataLog";
private final String[] REQUIRED_PERMISSIONS = {
Manifest.permission.WRITE_EXTERNAL_STORAGE,
//Manifest.permission.WRITE_EXTERNAL_STORAGE,
Manifest.permission.WAKE_LOCK,
};
@@ -107,14 +90,19 @@ public class OsdUtil implements ActivityCompat.OnRequestPermissionsResultCallbac
/**
* Based on http://stackoverflow.com/questions/7440473/android-how-to-check-if-the-intent-service-is-still-running-or-has-stopped-running
*/
private Context mContext;
private static Context mContext;
private Handler mHandler;
private String TAG = "OsdUtil";
private static String TAG = "OsdUtil";
private boolean mLogAlarms = true;
private boolean mLogSystem = true;
private boolean mLogData = true;
private boolean mPermissionsRequested = false;
private boolean mSMSPermissionsRequested = false;
private static final String mSysLogTableName = "SysLog";
//private LogManager mLm;
static private SQLiteDatabase mSysLogDb = null; // SQLite Database for data and log entries.
private final static Long mMinPruneInterval = new Long(5 * 60 * 1000); // minimum time between syslog pruning is 5 minutes
private static Long mLastPruneMillis = new Long(0); // Record of the last time we pruned the syslog db.
private static int mNbound = 0;
@@ -122,6 +110,9 @@ public class OsdUtil implements ActivityCompat.OnRequestPermissionsResultCallbac
mContext = context;
mHandler = handler;
updatePrefs();
//Log.i(TAG,"Creating Log Manager instance");
//mLm = new LogManager(mContext,false,false,null,0,0,false,0);
openDb();
writeToSysLogFile("OsdUtil() - initialised");
}
@@ -171,7 +162,7 @@ public class OsdUtil implements ActivityCompat.OnRequestPermissionsResultCallbac
}
}
if (nServers != 0) {
Log.v(TAG, "isServerRunning() - " + nServers + " instances are running");
//Log.v(TAG, "isServerRunning() - " + nServers + " instances are running");
return true;
} else
return false;
@@ -337,16 +328,18 @@ public class OsdUtil implements ActivityCompat.OnRequestPermissionsResultCallbac
/**
* Write a message to the system log file, provided mLogSystem is true.
* Write a message to the system log database.
*
* @param msgStr
*/
public void writeToSysLogFile(String msgStr) {
if (mLogSystem)
writeToLogFile(SYSLOG, msgStr);
else
Log.v(TAG, "writeToSysLogFile - mLogSystem False so not writing");
public void writeToSysLogFile(String msgStr,String logType) {
writeLogEntryToLocalDb(msgStr,logType);
}
public void writeToSysLogFile(String msgStr) {
writeLogEntryToLocalDb(msgStr,"v");
}
/**
* Write a message to the alarm log file, provided mLogAlarms is true.
@@ -572,4 +565,238 @@ public class OsdUtil implements ActivityCompat.OnRequestPermissionsResultCallbac
}
return(retVal);
}
private static boolean openDb() {
Log.d(TAG, "openDb");
try {
if (mSysLogDb == null) {
Log.i(TAG,"openDb: mSysLogDb is null - initialising");
mSysLogDb = new OsdSysLogHelper(mContext).getWritableDatabase();
} else {
Log.i(TAG,"openDb: mSysLogDb has been initialised already so not doing anything");
}
if (!checkTableExists(mSysLogDb, mSysLogTableName)) {
Log.e(TAG, "ERROR - Table "+mSysLogTableName+" does not exist");
return false;
} else {
Log.d(TAG, "table " + mSysLogTableName + " exists ok");
}
} catch (SQLException e) {
Log.e(TAG, "Failed to open Database: " + e.toString());
return false;
}
return true;
}
private static boolean checkTableExists(SQLiteDatabase osdDb, String osdTableName) {
Cursor c = null;
boolean tableExists = false;
Log.d(TAG, "checkTableExists()");
try {
c = osdDb.query(osdTableName, null,
null, null, null, null, null);
tableExists = true;
c.close();
} catch (Exception e) {
Log.d(TAG, osdTableName + " doesn't exist :(((");
}
return tableExists;
}
/**
* Write syslog string to local database
* FIXME - I am sure we should not be using raw SQL Srings to do this!
*/
public void writeLogEntryToLocalDb(String logText, String statusVal) {
Log.v(TAG, "writeLogEntryToLocalDb()");
Date curDate = new Date();
SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
String dateStr = dateFormat.format(curDate);
String SQLStr = "SQLStr";
try {
SQLStr = "INSERT INTO " + mSysLogTableName
+ "(dataTime, logLevel, dataJSON, uploaded)"
+ " VALUES("
+ "'" + dateStr + "',"
+ DatabaseUtils.sqlEscapeString(statusVal) + ","
+ DatabaseUtils.sqlEscapeString(logText) + ","
+ 0
+ ")";
mSysLogDb.execSQL(SQLStr);
Log.v(TAG, "syslog entry written to database: "+logText);
pruneSysLogDb();
} catch (SQLException e) {
Log.e(TAG, "writeLogEngryToLocalDb(): Error Writing Data: " + e.toString());
Log.e(TAG, "SQLStr was " + SQLStr);
}
}
/**
* Return an array list of objects representing the syslog entries in the database by calling the specified callback function.
*
* @return True on successful start or false if call fails.
*/
public boolean getSysLogList(Consumer<ArrayList<HashMap<String, String>>> callback) {
Log.v(TAG, "getSysLogList");
ArrayList<HashMap<String, String>> eventsList = new ArrayList<>();
String whereClause = "";
String[] whereArgs = {};
String[] columns = {"*"};
new SelectQueryTask(mSysLogTableName, columns, null, null,
null, null, "dataTime DESC", (Cursor cursor) -> {
Log.v(TAG, "getSysLogList - returned " + cursor);
if (cursor != null) {
Log.v(TAG, "getSysLogList - returned " + cursor.getCount() + " records");
while (!cursor.isAfterLast()) {
HashMap<String, String> event = new HashMap<>();
//event.put("id", cursor.getString(cursor.getColumnIndex("id")));
event.put("dataTime", cursor.getString(cursor.getColumnIndex("dataTime")));
String loglevel = cursor.getString(cursor.getColumnIndex("logLevel"));
event.put("loglevel", loglevel);
event.put("dataJSON", cursor.getString(cursor.getColumnIndex("dataJSON")));
//event.put("dataJSON", cursor.getString(cursor.getColumnIndex("dataJSON")));
eventsList.add(event);
cursor.moveToNext();
}
}
callback.accept(eventsList);
}).execute();
return (true);
}
/**
* Executes the sqlite query (=SELECT statement)
* Use as new SelectQueryTask(xxx,xxx,xx,xxxx).execute()
*
*/
static private class SelectQueryTask extends AsyncTask<Void, Void, Cursor> {
// Based on https://stackoverflow.com/a/21120199/2104584
String mTable;
String[] mColumns;
String mSelection;
String[] mSelectionArgs;
String mGroupBy;
String mHaving;
String mOrderBy;
Consumer<Cursor> mCallback;
//query(String table, String[] columns, String selection, String[] selectionArgs,
// String groupBy, String having, String orderBy)
SelectQueryTask(String table, String[] columns, String selection, String[] selectionArgs,
String groupBy, String having, String orderBy, Consumer<Cursor> callback) {
// list all the parameters like in normal class define
this.mTable = table;
this.mColumns = columns;
this.mSelection = selection;
this.mSelectionArgs = selectionArgs;
this.mGroupBy = groupBy;
this.mHaving = having;
this.mOrderBy = orderBy;
this.mCallback = callback;
}
@Override
protected Cursor doInBackground(Void... params) {
Log.v(TAG, "runSelect.doInBackground()");
Log.v(TAG, "SelectQueryTask.doInBackground: mTable=" + mTable + ", mColumns=" + Arrays.toString(mColumns)
+ ", mSelection=" + mSelection + ", mSelectionArgs=" + Arrays.toString(mSelectionArgs) + ", mGroupBy=" + mGroupBy
+ ", mHaving =" + mHaving + ", mOrderBy=" + mOrderBy);
try {
Cursor resultSet = mSysLogDb.query(mTable, mColumns, mSelection,
mSelectionArgs, mGroupBy, mHaving, mOrderBy);
resultSet.moveToFirst();
return (resultSet);
} catch (SQLException e) {
Log.e(TAG, "SelectQueryTask.doInBackground(): Error selecting Data: " + e.toString());
return (null);
} catch (IllegalArgumentException e) {
Log.e(TAG, "SelectQueryTask.doInBackground(): Illegal Argument Exception: " + e.toString());
return (null);
}
}
@Override
protected void onPostExecute(final Cursor result) {
mCallback.accept(result);
}
}
/**
* pruneSysLogDb() removes data that is older than 7 days
*/
public int pruneSysLogDb() {
//Log.v(TAG, "pruneSysLogDb()");
int retVal;
long currentDateMillis = new Date().getTime();
if (currentDateMillis > mLastPruneMillis + mMinPruneInterval) {
mLastPruneMillis = currentDateMillis;
// FIXME - change this to something sensible like 7 days after testing
long endDateMillis = currentDateMillis - 5 * 60 * 1000;
SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
String endDateStr = dateFormat.format(new Date(endDateMillis));
Log.v(TAG, "pruneSysLogDb - endDateStr=" + endDateStr);
try {
String selectStr = "DataTime<=?";
String[] selectArgs = {endDateStr};
retVal = mSysLogDb.delete(mSysLogTableName, selectStr, selectArgs);
} catch (Exception e) {
Log.e(TAG, "Error deleting log entries" + e.toString());
retVal = 0;
}
if (retVal > 0) {
Log.v(TAG, String.format("pruneSysLogDb() - deleted %d records", retVal));
}
return (retVal);
} else {
return (0);
}
}
public static class OsdSysLogHelper extends SQLiteOpenHelper {
// If you change the database schema, you must increment the database version.
public static final int DATABASE_VERSION = 1;
public static final String DATABASE_NAME = "OsdSysLog.db";
private static final String TAG = "LogManager.OsdSysLogHelper";
public OsdSysLogHelper(Context context) {
super(context, DATABASE_NAME, null, DATABASE_VERSION);
Log.d(TAG, "OsdSysLogHelper constructor");
}
public void onCreate(SQLiteDatabase db) {
Log.i(TAG, "onCreate - TableName=" + mSysLogTableName);
String SQLStr = "CREATE TABLE IF NOT EXISTS " + mSysLogTableName + "("
+ "id INTEGER PRIMARY KEY,"
+ "dataTime DATETIME,"
+ "logLevel TEXT,"
+ "dataJSON TEXT,"
+ "uploaded INT"
+ ");";
db.execSQL(SQLStr);
}
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
// This database is only a cache for online data, so its upgrade policy is
// to simply to discard the data and start over
Log.i(TAG,"onUpgrade()");
db.execSQL("Drop table if exists " + mSysLogTableName + ";");
onCreate(db);
}
public void onDowngrade(SQLiteDatabase db, int oldVersion, int newVersion) {
Log.i(TAG,"onDowngrade()");
onUpgrade(db, oldVersion, newVersion);
}
}
}

View File

@@ -439,6 +439,7 @@ public class SdServer extends Service implements SdDataReceiver {
if (mLm != null) {
Log.d(TAG, "Closing Down Log Manager");
mLm.stop();
mLm.close();
}
@@ -590,7 +591,7 @@ public class SdServer extends Service implements SdDataReceiver {
}
if (mLogAlarms) {
Log.v(TAG, "WARNING - Logging to SD Card");
writeAlarmToSD();
//writeAlarmToSD();
} else {
Log.v(TAG, "WARNING");
}
@@ -603,7 +604,7 @@ public class SdServer extends Service implements SdDataReceiver {
sdData.alarmStanding = true;
if (mLogAlarms) {
Log.v(TAG, "***ALARM*** - Logging to SD Card");
writeAlarmToSD();
//writeAlarmToSD();
} else {
Log.v(TAG, "***ALARM***");
}
@@ -641,7 +642,7 @@ public class SdServer extends Service implements SdDataReceiver {
sdData.fallAlarmStanding = true;
if (mLogAlarms) {
Log.v(TAG, "***FALL*** - Logging to SD Card");
writeAlarmToSD();
//writeAlarmToSD();
showNotification(2);
} else {
Log.v(TAG, "***FALL***");
@@ -675,7 +676,7 @@ public class SdServer extends Service implements SdDataReceiver {
sdData.alarmPhrase = "HR ABNORMAL";
if (mLogAlarms) {
Log.v(TAG, "***HEART RATE*** - Logging to SD Card");
writeAlarmToSD();
//writeAlarmToSD();
} else {
Log.v(TAG, "***HEART RATE***");
}
@@ -709,7 +710,7 @@ public class SdServer extends Service implements SdDataReceiver {
sdData.alarmPhrase = "Oxygen Saturation ABNORMAL";
if (mLogAlarms) {
Log.v(TAG, "***OXYGEN SATURATION*** - Logging to SD Card");
writeAlarmToSD();
//writeAlarmToSD();
} else {
Log.v(TAG, "***OXYGEN SATURATION***");
}
@@ -742,7 +743,7 @@ public class SdServer extends Service implements SdDataReceiver {
// Fault
if ((sdData.alarmState) == 4 || (sdData.alarmState == 7) || (sdData.mHRFaultStanding)) {
sdData.alarmPhrase = "FAULT";
writeAlarmToSD();
//writeAlarmToSD();
faultWarningBeep();
showNotification(-1);
} else {
@@ -1259,62 +1260,6 @@ public class SdServer extends Service implements SdDataReceiver {
}
/**
* Write data to SD card alarm log
*/
public void writeAlarmToSD() {
writeToSD(true);
}
/**
* Write to data log file on SD Card
*/
public void writeToSD() {
writeToSD(false);
}
/**
* Write data to SD card - writes to data log file unless alarm=true,
* in which case writes to alarm log file.
*/
public void writeToSD(boolean alarm) {
//Log.v(TAG, "writeToSD(" + alarm + ")");
Time tnow = new Time(Time.getCurrentTimezone());
tnow.setToNow();
String dateStr = tnow.format("%Y-%m-%d");
// Select filename depending on 'alarm' parameter.
String fname;
if (alarm)
fname = "AlarmLog";
else
fname = "DataLog";
fname = fname + "_" + dateStr + ".txt";
// Open output directory on SD Card.
if (mUtil.isExternalStorageWritable()) {
try {
FileWriter of = new FileWriter(getExternalFilesDir(null).toString()
+ "/" + fname, true);
if (mSdData != null) {
if (alarm) {
//Log.v(TAG, "writeToSD() - logging mSdData.toString()");
of.append(mSdData.toString() + "\n");
} else {
//Log.v(TAG, "writeToSD() - logging mSdData.toCSVString()");
of.append(mSdData.toCSVString(true) + "\n");
}
}
of.close();
} catch (Exception ex) {
Log.e(TAG, "writeAlarmToSD - error " + ex.toString());
}
} else {
Log.e(TAG, "ERROR - Can not Write to External Folder");
}
}
public void sendPhoneAlarm() {
/**
* Use the separate OpenSeizureDetector Dialler app to generate a phone call alarm to the numbers selected for SMS Alarms.

View File

@@ -69,27 +69,15 @@
android:checked="false"
android:onClick="onRadioButtonClicked"
android:text="@string/local_data" />
</RadioGroup>
<LinearLayout
android:visibility="gone"
android:id="@+id/local_data_ll"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<TextView
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="@string/EventsInLocalDb"
android:textAppearance="?android:attr/textAppearanceLarge" />
<ListView
android:id="@+id/eventLogListView"
<RadioButton
android:id="@+id/syslog_rb"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="10dp" />
</LinearLayout>
android:checked="false"
android:onClick="onRadioButtonClicked"
android:text="@string/system_logs" />
</RadioGroup>
<LinearLayout
android:visibility="visible"
@@ -138,7 +126,48 @@
android:layout_height="wrap_content"
android:layout_marginTop="10dp" />
</LinearLayout>
<LinearLayout
android:visibility="gone"
android:id="@+id/local_data_ll"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<TextView
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="@string/EventsInLocalDb"
android:textAppearance="?android:attr/textAppearanceLarge" />
<ListView
android:id="@+id/eventLogListView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="10dp" />
</LinearLayout>
<LinearLayout
android:visibility="gone"
android:id="@+id/syslog_ll"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<TextView
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="@string/system_logs"
android:textAppearance="?android:attr/textAppearanceLarge" />
<ListView
android:id="@+id/sysLogListView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="10dp" />
</LinearLayout>
</LinearLayout>

View File

@@ -0,0 +1,31 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<LinearLayout
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<TextView
android:id="@+id/syslog_entry_date_tv"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="date" />
<TextView
android:id="@+id/syslog_level_tv"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="---" />
</LinearLayout>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="date"
android:id="@+id/syslog_entry_text_tv" />
</LinearLayout>

View File

@@ -61,12 +61,12 @@
android:title="@string/test_sms_alarm_notification" />
</group>
<group android:id="@+id/grp5">
<item
<!--<item
android:id="@+id/action_logs"
android:enabled="true"
android:icon="@drawable/ic_action_settings"
android:showAsAction="never|withText"
android:title="@string/view_log_entries" />
android:title="@string/view_log_entries" /> -->
<!--
<item
android:enabled="false"

View File

@@ -348,4 +348,5 @@
<string name="prune_database">Prune Database</string>
<string name="check_seizures_message">Please select the events highlighted in pink to say if they are real seizures or false alarms</string>
<string name="error_server_not_running">ERROR: OpenSeizureDetector Server is not running - please re-start it</string>
<string name="system_logs">System Logs</string>
</resources>