From 7148121a36954f9401e0e1580bfbc071e3b8d50b Mon Sep 17 00:00:00 2001 From: Graham Jones Date: Tue, 22 Feb 2022 22:21:36 +0000 Subject: [PATCH] Moved system logging to use sqlite database rather than file - removed need for access external strage permission. --- .../org/openseizuredetector/LogManager.java | 147 ++++----- .../LogManagerControlActivity.java | 41 ++- .../org/openseizuredetector/MainActivity.java | 3 +- .../uk/org/openseizuredetector/OsdUtil.java | 303 +++++++++++++++--- .../uk/org/openseizuredetector/SdServer.java | 69 +--- .../layout/activity_log_manager_control.xml | 69 ++-- .../main/res/layout/syslog_entry_layout.xml | 31 ++ .../main/res/menu/main_activity_actions.xml | 4 +- app/src/main/res/values/strings.xml | 1 + 9 files changed, 450 insertions(+), 218 deletions(-) create mode 100644 app/src/main/res/layout/syslog_entry_layout.xml diff --git a/app/src/main/java/uk/org/openseizuredetector/LogManager.java b/app/src/main/java/uk/org/openseizuredetector/LogManager.java index f47d568..4f182b4 100644 --- a/app/src/main/java/uk/org/openseizuredetector/LogManager.java +++ b/app/src/main/java/uk/org/openseizuredetector/LogManager.java @@ -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); - startRemoteLogTimer(); + 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; } + return true; } - 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(); diff --git a/app/src/main/java/uk/org/openseizuredetector/LogManagerControlActivity.java b/app/src/main/java/uk/org/openseizuredetector/LogManagerControlActivity.java index a7b0729..bdc1f87 100644 --- a/app/src/main/java/uk/org/openseizuredetector/LogManagerControlActivity.java +++ b/app/src/main/java/uk/org/openseizuredetector/LogManagerControlActivity.java @@ -51,16 +51,16 @@ public class LogManagerControlActivity extends AppCompatActivity { private UiTimer mUiTimer; private ArrayList> mEventsList; private ArrayList> mRemoteEventsList; + private ArrayList> 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> 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,8 +351,17 @@ 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; } } diff --git a/app/src/main/java/uk/org/openseizuredetector/MainActivity.java b/app/src/main/java/uk/org/openseizuredetector/MainActivity.java index 9bf9e00..879de94 100644 --- a/app/src/main/java/uk/org/openseizuredetector/MainActivity.java +++ b/app/src/main/java/uk/org/openseizuredetector/MainActivity.java @@ -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 { diff --git a/app/src/main/java/uk/org/openseizuredetector/OsdUtil.java b/app/src/main/java/uk/org/openseizuredetector/OsdUtil.java index 9ad5947..850703a 100644 --- a/app/src/main/java/uk/org/openseizuredetector/OsdUtil.java +++ b/app/src/main/java/uk/org/openseizuredetector/OsdUtil.java @@ -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>> callback) { + Log.v(TAG, "getSysLogList"); + ArrayList> 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 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 { + // Based on https://stackoverflow.com/a/21120199/2104584 + String mTable; + String[] mColumns; + String mSelection; + String[] mSelectionArgs; + String mGroupBy; + String mHaving; + String mOrderBy; + Consumer 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 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); + } + } + } diff --git a/app/src/main/java/uk/org/openseizuredetector/SdServer.java b/app/src/main/java/uk/org/openseizuredetector/SdServer.java index 173d963..8798dfc 100644 --- a/app/src/main/java/uk/org/openseizuredetector/SdServer.java +++ b/app/src/main/java/uk/org/openseizuredetector/SdServer.java @@ -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. diff --git a/app/src/main/res/layout/activity_log_manager_control.xml b/app/src/main/res/layout/activity_log_manager_control.xml index 0e36915..ae67d34 100644 --- a/app/src/main/res/layout/activity_log_manager_control.xml +++ b/app/src/main/res/layout/activity_log_manager_control.xml @@ -69,27 +69,15 @@ android:checked="false" android:onClick="onRadioButtonClicked" android:text="@string/local_data" /> - - - - - - - + android:checked="false" + android:onClick="onRadioButtonClicked" + android:text="@string/system_logs" /> + + /> - + + + + + + + + + + + + + + + + diff --git a/app/src/main/res/layout/syslog_entry_layout.xml b/app/src/main/res/layout/syslog_entry_layout.xml new file mode 100644 index 0000000..e905a8c --- /dev/null +++ b/app/src/main/res/layout/syslog_entry_layout.xml @@ -0,0 +1,31 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/menu/main_activity_actions.xml b/app/src/main/res/menu/main_activity_actions.xml index 3c972cc..a4c4bbd 100644 --- a/app/src/main/res/menu/main_activity_actions.xml +++ b/app/src/main/res/menu/main_activity_actions.xml @@ -61,12 +61,12 @@ android:title="@string/test_sms_alarm_notification" /> - + android:title="@string/view_log_entries" /> -->