diff --git a/.travis.yml b/.travis.yml index c65a9c3..6d11e4c 100644 --- a/.travis.yml +++ b/.travis.yml @@ -3,11 +3,11 @@ language: android jdk: oraclejdk8 before_install: - - wget http://services.gradle.org/distributions/gradle-2.14.1-bin.zip - - unzip gradle-2.14.1-bin.zip - - export GRADLE_HOME=$PWD/gradle-2.14.1 + - wget http://services.gradle.org/distributions/gradle-3.3-bin.zip + - unzip gradle-3.3-bin.zip + - export GRADLE_HOME=$PWD/gradle-3.3 - export PATH=$GRADLE_HOME/bin:$PATH - - ( sleep 5 && while [ 1 ]; do sleep 1; echo y; done ) | android update sdk -a --no-ui --filter tool,platform-tool,build-tools-25.0.0 + - ( sleep 5 && while [ 1 ]; do sleep 1; echo y; done ) | android update sdk -a --no-ui --filter tool,platform-tool,build-tools-25.0.2 script: - gradle assembleDebug diff --git a/build.gradle b/build.gradle index e6359b3..6475517 100755 --- a/build.gradle +++ b/build.gradle @@ -6,7 +6,7 @@ buildscript { } dependencies { - classpath 'com.android.tools.build:gradle:2.2.3' + classpath 'com.android.tools.build:gradle:2.3.1' } } @@ -45,21 +45,21 @@ android { dependencies { compile fileTree(dir: 'libs', include: ['*.jar']) // testCompile 'junit:junit:4.12' - compile 'com.android.support:appcompat-v7:25.1.0' - compile 'com.android.support:recyclerview-v7:25.1.0' - compile 'com.android.support:cardview-v7:25.1.0' - compile 'com.google.android.gms:play-services-location:10.0.1' - compile 'com.google.android.gms:play-services-maps:10.0.1' - compile 'com.google.android.gms:play-services-nearby:10.0.1' - compile 'com.google.android.gms:play-services-places:10.0.1' - compile 'com.google.android.gms:play-services-awareness:10.0.1' + compile 'com.android.support:appcompat-v7:25.3.1' + compile 'com.android.support:recyclerview-v7:25.3.1' + compile 'com.android.support:cardview-v7:25.3.1' + compile 'com.google.android.gms:play-services-location:10.2.4' + compile 'com.google.android.gms:play-services-maps:10.2.4' + compile 'com.google.android.gms:play-services-nearby:10.2.4' + compile 'com.google.android.gms:play-services-places:10.2.4' + compile 'com.google.android.gms:play-services-awareness:10.2.4' compile 'com.google.maps.android:android-maps-utils:0.4' compile 'com.squareup.okhttp3:okhttp:3.2.0' compile 'commons-io:commons-io:2.4' compile 'commons-codec:commons-codec:1.10' compile 'org.apache.commons:commons-lang3:3.4' compile 'com.fasterxml.jackson.core:jackson-core:2.7.3' - compile 'com.github.philjay:mpandroidchart:v3.0.0' + compile 'com.github.philjay:mpandroidchart:v3.0.1' } buildTypes { diff --git a/res/values/databases.xml b/res/values/databases.xml index a6d7f01..672f1cd 100755 --- a/res/values/databases.xml +++ b/res/values/databases.xml @@ -12,4 +12,8 @@ CREATE TABLE history(_id INTEGER PRIMARY KEY AUTOINCREMENT, fetched INTEGER, transmitted INTEGER, observed INTEGER, direction TEXT, length INTEGER, body TEXT, number_name TEXT, number TEXT); + + + CREATE TABLE history(_id INTEGER PRIMARY KEY AUTOINCREMENT, fetched INTEGER, transmitted INTEGER, observed INTEGER, event_name TEXT, event_details TEXT); + \ No newline at end of file diff --git a/src/com/audacious_software/passive_data_kit/Logger.java b/src/com/audacious_software/passive_data_kit/Logger.java index b315574..9ea620e 100755 --- a/src/com/audacious_software/passive_data_kit/Logger.java +++ b/src/com/audacious_software/passive_data_kit/Logger.java @@ -2,7 +2,10 @@ import android.content.Context; +import com.audacious_software.passive_data_kit.generators.diagnostics.AppEvent; + import java.util.HashMap; +import java.util.Map; /** * Created by cjkarr on 4/3/2016. @@ -10,8 +13,12 @@ public class Logger { private Context mContext = null; - public void log(String event, HashMap details) { - // TODO + public void log(String event, Map details) { + if (details == null) { + details = new HashMap<>(); + } + + AppEvent.getInstance(this.mContext).logEvent(event, details); } private static class LoggerHolder { diff --git a/src/com/audacious_software/passive_data_kit/activities/DataStreamActivity.java b/src/com/audacious_software/passive_data_kit/activities/DataStreamActivity.java index 8815505..3e80bb0 100755 --- a/src/com/audacious_software/passive_data_kit/activities/DataStreamActivity.java +++ b/src/com/audacious_software/passive_data_kit/activities/DataStreamActivity.java @@ -58,7 +58,7 @@ protected void onPause() { } @Override - public void onGeneratorUpdated(String identifier, Bundle data) { + public void onGeneratorUpdated(String identifier, long timestamp, Bundle data) { final DataStreamActivity me = this; this.runOnUiThread(new Runnable() { diff --git a/src/com/audacious_software/passive_data_kit/generators/Generator.java b/src/com/audacious_software/passive_data_kit/generators/Generator.java index 4adc72e..66563c0 100755 --- a/src/com/audacious_software/passive_data_kit/generators/Generator.java +++ b/src/com/audacious_software/passive_data_kit/generators/Generator.java @@ -3,6 +3,7 @@ import android.content.ContentValues; import android.content.Context; import android.database.Cursor; +import android.database.MatrixCursor; import android.database.sqlite.SQLiteDatabase; import android.os.Bundle; import android.view.LayoutInflater; @@ -120,6 +121,12 @@ public static String formatTimestamp(Context context, double timestamp) { public abstract List fetchPayloads(); + public Cursor queryHistory(String[] cols, String where, String[] args, String orderBy) { + Cursor c = new MatrixCursor(cols); + + return c; + } + protected int getDatabaseVersion(SQLiteDatabase db) { String where = "type = ? AND name = ?"; String[] args = { "table", Generator.TABLE_METADATA }; diff --git a/src/com/audacious_software/passive_data_kit/generators/Generators.java b/src/com/audacious_software/passive_data_kit/generators/Generators.java index ddac98b..384e0f1 100755 --- a/src/com/audacious_software/passive_data_kit/generators/Generators.java +++ b/src/com/audacious_software/passive_data_kit/generators/Generators.java @@ -11,6 +11,7 @@ import com.audacious_software.passive_data_kit.Logger; import com.audacious_software.passive_data_kit.diagnostics.DiagnosticAction; +import com.audacious_software.passive_data_kit.generators.diagnostics.AppEvent; import com.audacious_software.pdk.passivedatakit.R; import java.lang.reflect.InvocationTargetException; @@ -37,6 +38,8 @@ public void start() { { this.mGenerators.clear(); + this.mGenerators.add(AppEvent.class.getCanonicalName()); + for (String className : this.mContext.getResources().getStringArray(R.array.pdk_available_generators)) { this.mGenerators.add(className); @@ -238,9 +241,19 @@ public List> activeGenerators() { return active; } - public void notifyGeneratorUpdated(String identifier, Bundle bundle) { + public void notifyGeneratorUpdated(String identifier, long timestamp, Bundle bundle) { for (GeneratorUpdatedListener listener : this.mGeneratorUpdatedListeners) { - listener.onGeneratorUpdated(identifier, bundle); + listener.onGeneratorUpdated(identifier, timestamp, bundle); + } + } + + public void notifyGeneratorUpdated(String identifier, Bundle bundle) { + long timestamp = System.currentTimeMillis(); + + synchronized(this.mGeneratorUpdatedListeners) { + for (GeneratorUpdatedListener listener : this.mGeneratorUpdatedListeners) { + listener.onGeneratorUpdated(identifier, timestamp, bundle); + } } } @@ -262,14 +275,18 @@ private void setContext(Context context) { } public void addNewGeneratorUpdatedListener(Generators.GeneratorUpdatedListener listener) { - this.mGeneratorUpdatedListeners.add(listener); + synchronized(this.mGeneratorUpdatedListeners) { + this.mGeneratorUpdatedListeners.add(listener); + } } public void removeGeneratorUpdatedListener(Generators.GeneratorUpdatedListener listener) { - this.mGeneratorUpdatedListeners.remove(listener); + synchronized(this.mGeneratorUpdatedListeners) { + this.mGeneratorUpdatedListeners.remove(listener); + } } public interface GeneratorUpdatedListener { - void onGeneratorUpdated(String identifier, Bundle data); + void onGeneratorUpdated(String identifier, long timestamp, Bundle data); } } diff --git a/src/com/audacious_software/passive_data_kit/generators/communication/PhoneCalls.java b/src/com/audacious_software/passive_data_kit/generators/communication/PhoneCalls.java index f72cbe8..cea19b6 100755 --- a/src/com/audacious_software/passive_data_kit/generators/communication/PhoneCalls.java +++ b/src/com/audacious_software/passive_data_kit/generators/communication/PhoneCalls.java @@ -171,130 +171,132 @@ public void run() { Cursor c = me.mContext.getContentResolver().query(CallLog.Calls.CONTENT_URI, null, where, args, CallLog.Calls.DATE); - while (c.moveToNext()) { - ContentValues values = new ContentValues(); - values.put(PhoneCalls.HISTORY_OBSERVED, c.getLong(c.getColumnIndex(CallLog.Calls.DATE))); - values.put(PhoneCalls.HISTORY_DURATION, c.getLong(c.getColumnIndex(CallLog.Calls.DURATION))); - values.put(PhoneCalls.HISTORY_NUMBER, c.getLong(c.getColumnIndex(CallLog.Calls.DURATION))); - values.put(PhoneCalls.HISTORY_IS_NEW, (c.getInt(c.getColumnIndex(CallLog.Calls.NEW)) != 0)); - - Bundle bundle = new Bundle(); - bundle.putLong(PhoneCalls.CALL_DATE_KEY, c.getLong(c.getColumnIndex(CallLog.Calls.DATE))); - bundle.putLong(PhoneCalls.CALL_DURATION_KEY, c.getLong(c.getColumnIndex(CallLog.Calls.DURATION))); - bundle.putString(PhoneCalls.CALL_NUMBER_KEY, c.getString(c.getColumnIndex(CallLog.Calls.NUMBER))); - bundle.putBoolean(PhoneCalls.CALL_IS_NEW_KEY, (c.getInt(c.getColumnIndex(CallLog.Calls.NEW)) != 0)); - - int features = 0; - - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { - features = c.getInt(c.getColumnIndex(CallLog.Calls.FEATURES)); - } + if (c != null) { + while (c.moveToNext()) { + ContentValues values = new ContentValues(); + values.put(PhoneCalls.HISTORY_OBSERVED, c.getLong(c.getColumnIndex(CallLog.Calls.DATE))); + values.put(PhoneCalls.HISTORY_DURATION, c.getLong(c.getColumnIndex(CallLog.Calls.DURATION))); + values.put(PhoneCalls.HISTORY_NUMBER, c.getLong(c.getColumnIndex(CallLog.Calls.DURATION))); + values.put(PhoneCalls.HISTORY_IS_NEW, (c.getInt(c.getColumnIndex(CallLog.Calls.NEW)) != 0)); + + Bundle bundle = new Bundle(); + bundle.putLong(PhoneCalls.CALL_DATE_KEY, c.getLong(c.getColumnIndex(CallLog.Calls.DATE))); + bundle.putLong(PhoneCalls.CALL_DURATION_KEY, c.getLong(c.getColumnIndex(CallLog.Calls.DURATION))); + bundle.putString(PhoneCalls.CALL_NUMBER_KEY, c.getString(c.getColumnIndex(CallLog.Calls.NUMBER))); + bundle.putBoolean(PhoneCalls.CALL_IS_NEW_KEY, (c.getInt(c.getColumnIndex(CallLog.Calls.NEW)) != 0)); + + int features = 0; + + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { + features = c.getInt(c.getColumnIndex(CallLog.Calls.FEATURES)); + } - int typeInt = c.getInt(c.getColumnIndex(CallLog.Calls.TYPE)); - String type = PhoneCalls.CALL_TYPE_UNKNOWN; + int typeInt = c.getInt(c.getColumnIndex(CallLog.Calls.TYPE)); + String type = PhoneCalls.CALL_TYPE_UNKNOWN; + + switch (Build.VERSION.SDK_INT) { + case 25: + if (typeInt == CallLog.Calls.ANSWERED_EXTERNALLY_TYPE) { + type = PhoneCalls.CALL_TYPE_ANSWERED_EXTERNALLY; + } + + bundle.putBoolean(PhoneCalls.CALL_PULLED_EXTERNALLY_KEY, ((features & CallLog.Calls.FEATURES_PULLED_EXTERNALLY) == CallLog.Calls.FEATURES_PULLED_EXTERNALLY)); + + values.put(PhoneCalls.HISTORY_PULLED_EXTERNALLY, ((features & CallLog.Calls.FEATURES_PULLED_EXTERNALLY) == CallLog.Calls.FEATURES_PULLED_EXTERNALLY)); + case 24: + if (typeInt == CallLog.Calls.REJECTED_TYPE) { + type = PhoneCalls.CALL_TYPE_REJECTED; + } else if (typeInt == CallLog.Calls.BLOCKED_TYPE) { + type = PhoneCalls.CALL_TYPE_BLOCKED; + } + + bundle.putString(PhoneCalls.CALL_POST_DIAL_DIGITS_KEY, c.getString(c.getColumnIndex(CallLog.Calls.POST_DIAL_DIGITS))); + bundle.putString(PhoneCalls.CALL_VIA_NUMBER_KEY, c.getString(c.getColumnIndex(CallLog.Calls.VIA_NUMBER))); + + values.put(PhoneCalls.HISTORY_POST_DIAL_DIGITS, c.getString(c.getColumnIndex(CallLog.Calls.POST_DIAL_DIGITS))); + values.put(PhoneCalls.HISTORY_VIA_NUMBER, c.getString(c.getColumnIndex(CallLog.Calls.VIA_NUMBER))); + case 21: + if (typeInt == CallLog.Calls.VOICEMAIL_TYPE) { + type = PhoneCalls.CALL_TYPE_VOICEMAIL; + } + + bundle.putString(PhoneCalls.CALL_COUNTRY_ISO_KEY, c.getString(c.getColumnIndex(CallLog.Calls.COUNTRY_ISO))); + bundle.putLong(PhoneCalls.CALL_DATA_USAGE_KEY, c.getLong(c.getColumnIndex(CallLog.Calls.DATA_USAGE))); + bundle.putString(PhoneCalls.CALL_GEOCODED_LOCATION_KEY, c.getString(c.getColumnIndex(CallLog.Calls.GEOCODED_LOCATION))); + bundle.putBoolean(PhoneCalls.CALL_VIDEO_KEY, ((features & CallLog.Calls.FEATURES_VIDEO) == CallLog.Calls.FEATURES_VIDEO)); + + values.put(PhoneCalls.HISTORY_COUNTRY_ISO, c.getString(c.getColumnIndex(CallLog.Calls.COUNTRY_ISO))); + values.put(PhoneCalls.HISTORY_DATA_USAGE, c.getLong(c.getColumnIndex(CallLog.Calls.DATA_USAGE))); + values.put(PhoneCalls.HISTORY_GEOCODED_LOCATION, c.getString(c.getColumnIndex(CallLog.Calls.GEOCODED_LOCATION))); + values.put(PhoneCalls.HISTORY_VIDEO, ((features & CallLog.Calls.FEATURES_VIDEO) == CallLog.Calls.FEATURES_VIDEO)); - switch(Build.VERSION.SDK_INT) { - case 25: - if (typeInt == CallLog.Calls.ANSWERED_EXTERNALLY_TYPE) { - type = PhoneCalls.CALL_TYPE_ANSWERED_EXTERNALLY; - } - - bundle.putBoolean(PhoneCalls.CALL_PULLED_EXTERNALLY_KEY, ((features & CallLog.Calls.FEATURES_PULLED_EXTERNALLY) == CallLog.Calls.FEATURES_PULLED_EXTERNALLY)); +// bundle.putString(PhoneCalls.CALL_TRANSCRIPTION_KEY, c.getString(c.getColumnIndex(CallLog.Calls.TRANSCRIPTION))); + case 19: + switch (c.getInt(c.getColumnIndex(CallLog.Calls.NUMBER_PRESENTATION))) { + case CallLog.Calls.PRESENTATION_ALLOWED: + bundle.putString(PhoneCalls.CALL_PRESENTATION_KEY, PhoneCalls.CALL_PRESENTATION_ALLOWED); + values.put(PhoneCalls.HISTORY_PRESENTATION, PhoneCalls.CALL_PRESENTATION_ALLOWED); + break; + case CallLog.Calls.PRESENTATION_RESTRICTED: + bundle.putString(PhoneCalls.CALL_PRESENTATION_KEY, PhoneCalls.CALL_PRESENTATION_RESTRICTED); + values.put(PhoneCalls.HISTORY_PRESENTATION, PhoneCalls.CALL_PRESENTATION_RESTRICTED); + break; + case CallLog.Calls.PRESENTATION_PAYPHONE: + bundle.putString(PhoneCalls.CALL_PRESENTATION_KEY, PhoneCalls.CALL_PRESENTATION_PAYPHONE); + values.put(PhoneCalls.HISTORY_PRESENTATION, PhoneCalls.CALL_PRESENTATION_PAYPHONE); + break; + case CallLog.Calls.PRESENTATION_UNKNOWN: + bundle.putString(PhoneCalls.CALL_PRESENTATION_KEY, PhoneCalls.CALL_PRESENTATION_UNKNOWN); + values.put(PhoneCalls.HISTORY_PRESENTATION, PhoneCalls.CALL_PRESENTATION_UNKNOWN); + break; + } + case 14: + bundle.putBoolean(PhoneCalls.CALL_IS_READ_KEY, (c.getInt(c.getColumnIndex(CallLog.Calls.IS_READ)) != 0)); + values.put(PhoneCalls.HISTORY_IS_READ, (c.getInt(c.getColumnIndex(CallLog.Calls.IS_READ)) != 0)); + case 1: + if (typeInt == CallLog.Calls.INCOMING_TYPE) { + type = PhoneCalls.CALL_TYPE_INCOMING; + } else if (typeInt == CallLog.Calls.OUTGOING_TYPE) { + type = PhoneCalls.CALL_TYPE_OUTGOING; + } else if (typeInt == CallLog.Calls.MISSED_TYPE) { + type = PhoneCalls.CALL_TYPE_MISSED; + } + } - values.put(PhoneCalls.HISTORY_PULLED_EXTERNALLY, ((features & CallLog.Calls.FEATURES_PULLED_EXTERNALLY) == CallLog.Calls.FEATURES_PULLED_EXTERNALLY)); - case 24: - if (typeInt == CallLog.Calls.REJECTED_TYPE) { - type = PhoneCalls.CALL_TYPE_REJECTED; - } else if (typeInt == CallLog.Calls.BLOCKED_TYPE) { - type = PhoneCalls.CALL_TYPE_BLOCKED; - } + bundle.putString(PhoneCalls.CALL_TYPE_KEY, type); + values.put(PhoneCalls.HISTORY_CALL_TYPE, type); - bundle.putString(PhoneCalls.CALL_POST_DIAL_DIGITS_KEY, c.getString(c.getColumnIndex(CallLog.Calls.POST_DIAL_DIGITS))); - bundle.putString(PhoneCalls.CALL_VIA_NUMBER_KEY, c.getString(c.getColumnIndex(CallLog.Calls.VIA_NUMBER))); + String[] sensitiveFields = { + PhoneCalls.CALL_NUMBER_KEY, + PhoneCalls.CALL_POST_DIAL_DIGITS_KEY, + PhoneCalls.CALL_VIA_NUMBER_KEY, + }; - values.put(PhoneCalls.HISTORY_POST_DIAL_DIGITS, c.getString(c.getColumnIndex(CallLog.Calls.POST_DIAL_DIGITS))); - values.put(PhoneCalls.HISTORY_VIA_NUMBER, c.getString(c.getColumnIndex(CallLog.Calls.VIA_NUMBER))); - case 21: - if (typeInt == CallLog.Calls.VOICEMAIL_TYPE) { - type = PhoneCalls.CALL_TYPE_VOICEMAIL; + for (String field : sensitiveFields) { + if (bundle.containsKey(field)) { + bundle.putString(field, new String(Hex.encodeHex(DigestUtils.sha256(bundle.getString(field))))); } + } - bundle.putString(PhoneCalls.CALL_COUNTRY_ISO_KEY, c.getString(c.getColumnIndex(CallLog.Calls.COUNTRY_ISO))); - bundle.putLong(PhoneCalls.CALL_DATA_USAGE_KEY, c.getLong(c.getColumnIndex(CallLog.Calls.DATA_USAGE))); - bundle.putString(PhoneCalls.CALL_GEOCODED_LOCATION_KEY, c.getString(c.getColumnIndex(CallLog.Calls.GEOCODED_LOCATION))); - bundle.putBoolean(PhoneCalls.CALL_VIDEO_KEY, ((features & CallLog.Calls.FEATURES_VIDEO) == CallLog.Calls.FEATURES_VIDEO)); - - values.put(PhoneCalls.HISTORY_COUNTRY_ISO, c.getString(c.getColumnIndex(CallLog.Calls.COUNTRY_ISO))); - values.put(PhoneCalls.HISTORY_DATA_USAGE, c.getLong(c.getColumnIndex(CallLog.Calls.DATA_USAGE))); - values.put(PhoneCalls.HISTORY_GEOCODED_LOCATION, c.getString(c.getColumnIndex(CallLog.Calls.GEOCODED_LOCATION))); - values.put(PhoneCalls.HISTORY_VIDEO, ((features & CallLog.Calls.FEATURES_VIDEO) == CallLog.Calls.FEATURES_VIDEO)); + String[] valueSensitiveFields = { + PhoneCalls.HISTORY_NUMBER, + PhoneCalls.HISTORY_POST_DIAL_DIGITS, + PhoneCalls.HISTORY_VIA_NUMBER, + }; -// bundle.putString(PhoneCalls.CALL_TRANSCRIPTION_KEY, c.getString(c.getColumnIndex(CallLog.Calls.TRANSCRIPTION))); - case 19: - switch (c.getInt(c.getColumnIndex(CallLog.Calls.NUMBER_PRESENTATION))) { - case CallLog.Calls.PRESENTATION_ALLOWED: - bundle.putString(PhoneCalls.CALL_PRESENTATION_KEY, PhoneCalls.CALL_PRESENTATION_ALLOWED); - values.put(PhoneCalls.HISTORY_PRESENTATION, PhoneCalls.CALL_PRESENTATION_ALLOWED); - break; - case CallLog.Calls.PRESENTATION_RESTRICTED: - bundle.putString(PhoneCalls.CALL_PRESENTATION_KEY, PhoneCalls.CALL_PRESENTATION_RESTRICTED); - values.put(PhoneCalls.HISTORY_PRESENTATION, PhoneCalls.CALL_PRESENTATION_RESTRICTED); - break; - case CallLog.Calls.PRESENTATION_PAYPHONE: - bundle.putString(PhoneCalls.CALL_PRESENTATION_KEY, PhoneCalls.CALL_PRESENTATION_PAYPHONE); - values.put(PhoneCalls.HISTORY_PRESENTATION, PhoneCalls.CALL_PRESENTATION_PAYPHONE); - break; - case CallLog.Calls.PRESENTATION_UNKNOWN: - bundle.putString(PhoneCalls.CALL_PRESENTATION_KEY, PhoneCalls.CALL_PRESENTATION_UNKNOWN); - values.put(PhoneCalls.HISTORY_PRESENTATION, PhoneCalls.CALL_PRESENTATION_UNKNOWN); - break; + for (String field : valueSensitiveFields) { + if (values.containsKey(field)) { + values.put(field, new String(Hex.encodeHex(DigestUtils.sha256(values.getAsString(field))))); } - case 14: - bundle.putBoolean(PhoneCalls.CALL_IS_READ_KEY, (c.getInt(c.getColumnIndex(CallLog.Calls.IS_READ)) != 0)); - values.put(PhoneCalls.HISTORY_IS_READ, (c.getInt(c.getColumnIndex(CallLog.Calls.IS_READ)) != 0)); - case 1: - if (typeInt == CallLog.Calls.INCOMING_TYPE) { - type = PhoneCalls.CALL_TYPE_INCOMING; - } else if (typeInt == CallLog.Calls.OUTGOING_TYPE) { - type = PhoneCalls.CALL_TYPE_OUTGOING; - } else if (typeInt == CallLog.Calls.MISSED_TYPE) { - type = PhoneCalls.CALL_TYPE_MISSED; - } - } - - bundle.putString(PhoneCalls.CALL_TYPE_KEY, type); - values.put(PhoneCalls.HISTORY_CALL_TYPE, type); - - String[] sensitiveFields = { - PhoneCalls.CALL_NUMBER_KEY, - PhoneCalls.CALL_POST_DIAL_DIGITS_KEY, - PhoneCalls.CALL_VIA_NUMBER_KEY, - }; - - for (String field : sensitiveFields) { - if (bundle.containsKey(field)) { - bundle.putString(field, new String(Hex.encodeHex(DigestUtils.sha256(bundle.getString(field))))); } - } - String[] valueSensitiveFields = { - PhoneCalls.HISTORY_NUMBER, - PhoneCalls.HISTORY_POST_DIAL_DIGITS, - PhoneCalls.HISTORY_VIA_NUMBER, - }; + me.mDatabase.insert(PhoneCalls.TABLE_HISTORY, null, values); - for (String field : valueSensitiveFields) { - if (values.containsKey(field)) { - values.put(field, new String(Hex.encodeHex(DigestUtils.sha256(values.getAsString(field))))); - } + Generators.getInstance(me.mContext).notifyGeneratorUpdated(PhoneCalls.GENERATOR_IDENTIFIER, bundle); } - me.mDatabase.insert(PhoneCalls.TABLE_HISTORY, null, values); - - Generators.getInstance(me.mContext).notifyGeneratorUpdated(PhoneCalls.GENERATOR_IDENTIFIER, bundle); + c.close(); } - - c.close(); } if (me.mHandler != null) { diff --git a/src/com/audacious_software/passive_data_kit/generators/device/Location.java b/src/com/audacious_software/passive_data_kit/generators/device/Location.java index b85f6d2..c008de5 100755 --- a/src/com/audacious_software/passive_data_kit/generators/device/Location.java +++ b/src/com/audacious_software/passive_data_kit/generators/device/Location.java @@ -10,6 +10,7 @@ import android.content.pm.PackageManager; import android.content.res.ColorStateList; import android.database.Cursor; +import android.database.MatrixCursor; import android.database.sqlite.SQLiteDatabase; import android.graphics.Bitmap; import android.graphics.drawable.Drawable; @@ -119,15 +120,15 @@ public class Location extends Generator implements GoogleApiClient.ConnectionCal private static int DATABASE_VERSION = 1; private static final String TABLE_HISTORY = "history"; - private static final String HISTORY_OBSERVED = "observed"; - private static final String HISTORY_LATITUDE = "latitude"; - private static final String HISTORY_LONGITUDE = "longitude"; - private static final String HISTORY_ALTITUDE = "altitude"; - private static final String HISTORY_BEARING = "bearing"; - private static final String HISTORY_SPEED = "speed"; - private static final String HISTORY_PROVIDER = "provider"; - private static final String HISTORY_LOCATION_TIMESTAMP = "location_timestamp"; - private static final String HISTORY_ACCURACY = "accuracy"; + public static final String HISTORY_OBSERVED = "observed"; + public static final String HISTORY_LATITUDE = "latitude"; + public static final String HISTORY_LONGITUDE = "longitude"; + public static final String HISTORY_ALTITUDE = "altitude"; + public static final String HISTORY_BEARING = "bearing"; + public static final String HISTORY_SPEED = "speed"; + public static final String HISTORY_PROVIDER = "provider"; + public static final String HISTORY_LOCATION_TIMESTAMP = "location_timestamp"; + public static final String HISTORY_ACCURACY = "accuracy"; public static Location getInstance(Context context) { if (Location.sInstance == null) { @@ -1007,4 +1008,8 @@ public void setUpdateInterval(long interval) { this.stopGenerator(); this.startGenerator(); } + + public Cursor queryHistory(String[] cols, String where, String[] args, String orderBy) { + return this.mDatabase.query(Location.TABLE_HISTORY, cols, where, args, null, null, orderBy); + } } diff --git a/src/com/audacious_software/passive_data_kit/generators/device/ScreenState.java b/src/com/audacious_software/passive_data_kit/generators/device/ScreenState.java index 2107447..d8a7432 100755 --- a/src/com/audacious_software/passive_data_kit/generators/device/ScreenState.java +++ b/src/com/audacious_software/passive_data_kit/generators/device/ScreenState.java @@ -37,17 +37,18 @@ public class ScreenState extends Generator{ private static final String ENABLED = "com.audacious_software.passive_data_kit.generators.device.ScreenState.ENABLED"; private static final boolean ENABLED_DEFAULT = true; - private static final String STATE_DOZE = "doze"; - private static final String STATE_DOZE_SUSPEND = "doze_suspend"; - private static final String STATE_ON = "on"; - private static final String STATE_OFF = "off"; - private static final String STATE_UNKNOWN = "unknown"; + public static final String STATE_DOZE = "doze"; + public static final String STATE_DOZE_SUSPEND = "doze_suspend"; + public static final String STATE_ON = "on"; + public static final String STATE_OFF = "off"; + public static final String STATE_UNKNOWN = "unknown"; private static final String DATABASE_PATH = "pdk-screen-state.sqlite"; private static final int DATABASE_VERSION = 2; - private static final String HISTORY_OBSERVED = "observed"; - private static final String HISTORY_STATE = "state"; - private static final String TABLE_HISTORY = "history"; + + public static final String HISTORY_OBSERVED = "observed"; + public static final String HISTORY_STATE = "state"; + public static final String TABLE_HISTORY = "history"; private static ScreenState sInstance = null; @@ -439,4 +440,8 @@ public static long latestPointGenerated(Context context) { return timestamp; } + + public Cursor queryHistory(String[] cols, String where, String[] args, String orderBy) { + return this.mDatabase.query(ScreenState.TABLE_HISTORY, cols, where, args, null, null, orderBy); + } } diff --git a/src/com/audacious_software/passive_data_kit/generators/diagnostics/AppEvent.java b/src/com/audacious_software/passive_data_kit/generators/diagnostics/AppEvent.java new file mode 100755 index 0000000..e92d291 --- /dev/null +++ b/src/com/audacious_software/passive_data_kit/generators/diagnostics/AppEvent.java @@ -0,0 +1,439 @@ +package com.audacious_software.passive_data_kit.generators.diagnostics; + +import android.content.ContentValues; +import android.content.Context; +import android.content.SharedPreferences; +import android.database.Cursor; +import android.database.sqlite.SQLiteDatabase; +import android.os.Bundle; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; + +import com.audacious_software.passive_data_kit.PassiveDataKit; +import com.audacious_software.passive_data_kit.activities.generators.DataPointViewHolder; +import com.audacious_software.passive_data_kit.diagnostics.DiagnosticAction; +import com.audacious_software.passive_data_kit.generators.Generator; +import com.audacious_software.passive_data_kit.generators.Generators; +import com.audacious_software.pdk.passivedatakit.R; + +import org.json.JSONException; +import org.json.JSONObject; + +import java.io.File; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +public class AppEvent extends Generator{ + private static final String GENERATOR_IDENTIFIER = "pdk-app-event"; + + private static final String ENABLED = "com.audacious_software.passive_data_kit.generators.diagnostics.AppEvent.ENABLED"; + private static final boolean ENABLED_DEFAULT = true; + + private static final String DATABASE_PATH = "pdk-app-event.sqlite"; + private static final int DATABASE_VERSION = 1; + + public static final String HISTORY_OBSERVED = "observed"; + public static final String HISTORY_EVENT_NAME = "event_name"; + public static final String HISTORY_EVENT_DETAILS = "event_details"; + public static final String TABLE_HISTORY = "history"; + + private static AppEvent sInstance = null; + + private SQLiteDatabase mDatabase = null; + + public static AppEvent getInstance(Context context) { + if (AppEvent.sInstance == null) { + AppEvent.sInstance = new AppEvent(context.getApplicationContext()); + } + + return AppEvent.sInstance; + } + + public AppEvent(Context context) { + super(context); + } + + public static void start(final Context context) { + AppEvent.getInstance(context).startGenerator(); + } + + private void startGenerator() { + final AppEvent me = this; + + Generators.getInstance(this.mContext).registerCustomViewClass(AppEvent.GENERATOR_IDENTIFIER, AppEvent.class); + + File path = PassiveDataKit.getGeneratorsStorage(this.mContext); + + path = new File(path, AppEvent.DATABASE_PATH); + + this.mDatabase = SQLiteDatabase.openOrCreateDatabase(path, null); + + int version = this.getDatabaseVersion(this.mDatabase); + + switch (version) { + case 0: + this.mDatabase.execSQL(this.mContext.getString(R.string.pdk_generator_app_events_create_history_table)); + } + + this.setDatabaseVersion(this.mDatabase, AppEvent.DATABASE_VERSION); + } + + public static boolean isEnabled(Context context) { + SharedPreferences prefs = Generators.getInstance(context).getSharedPreferences(context); + + return prefs.getBoolean(AppEvent.ENABLED, AppEvent.ENABLED_DEFAULT); + } + + public static boolean isRunning(Context context) { + return (AppEvent.sInstance != null); + } + + public static ArrayList diagnostics(Context context) { + return new ArrayList<>(); + } + + public static void bindViewHolder(DataPointViewHolder holder) { +/* final Context context = holder.itemView.getContext(); + + Calendar cal = Calendar.getInstance(); + cal.set(Calendar.HOUR_OF_DAY, 0); + cal.set(Calendar.MINUTE, 0); + cal.set(Calendar.SECOND, 0); + cal.set(Calendar.MILLISECOND, 0); + + long zeroStart = cal.getTimeInMillis(); + cal.add(Calendar.DATE, -1); + + LinearLayout zeroTimeline = (LinearLayout) holder.itemView.findViewById(R.id.day_zero_value); + + AppEvents.populateTimeline(context, zeroTimeline, zeroStart); + + long oneStart = cal.getTimeInMillis(); + cal.add(Calendar.DATE, -1); + + LinearLayout oneTimeline = (LinearLayout) holder.itemView.findViewById(R.id.day_one_value); + + AppEvents.populateTimeline(context, oneTimeline, oneStart); + + long twoStart = cal.getTimeInMillis(); + + LinearLayout twoTimeline = (LinearLayout) holder.itemView.findViewById(R.id.day_two_value); + + AppEvents.populateTimeline(context, twoTimeline, twoStart); + + AppEvents generator = AppEvents.getInstance(context); + + Cursor c = generator.mDatabase.query(AppEvents.TABLE_HISTORY, null, null, null, null, null, AppEvents.HISTORY_OBSERVED + " DESC"); + + View cardContent = holder.itemView.findViewById(R.id.card_content); + View cardEmpty = holder.itemView.findViewById(R.id.card_empty); + TextView dateLabel = (TextView) holder.itemView.findViewById(R.id.generator_data_point_date); + + if (c.moveToNext()) { + cardContent.setVisibility(View.VISIBLE); + cardEmpty.setVisibility(View.GONE); + + long timestamp = c.getLong(c.getColumnIndex(AppEvents.HISTORY_OBSERVED)) / 1000; + + dateLabel.setText(Generator.formatTimestamp(context, timestamp)); + + cal = Calendar.getInstance(); + DateFormat format = android.text.format.DateFormat.getDateFormat(context); + + TextView zeroDayLabel = (TextView) holder.itemView.findViewById(R.id.day_zero_label); + zeroDayLabel.setText(format.format(cal.getTime())); + + cal.add(Calendar.DATE, -1); + + TextView oneDayLabel = (TextView) holder.itemView.findViewById(R.id.day_one_label); + oneDayLabel.setText(format.format(cal.getTime())); + + cal.add(Calendar.DATE, -1); + + TextView twoDayLabel = (TextView) holder.itemView.findViewById(R.id.day_two_label); + twoDayLabel.setText(format.format(cal.getTime())); + } else { + cardContent.setVisibility(View.GONE); + cardEmpty.setVisibility(View.VISIBLE); + + dateLabel.setText(R.string.label_never_pdk); + } + + c.close(); + */ + } + + /* + private static void populateTimeline(Context context, LinearLayout timeline, long start) { + timeline.removeAllViews(); + + AppEvents generator = AppEvents.getInstance(context); + + long end = start + (24 * 60 * 60 * 1000); + + String where = AppEvents.HISTORY_OBSERVED + " >= ? AND " + AppEvents.HISTORY_OBSERVED + " < ?"; + String[] args = { "" + start, "" + end }; + + Cursor c = generator.mDatabase.query(AppEvents.TABLE_HISTORY, null, where, args, null, null, AppEvents.HISTORY_OBSERVED); + + ArrayList activeStates = new ArrayList<>(); + ArrayList activeTimestamps = new ArrayList<>(); + + while (c.moveToNext()) { + long timestamp = c.getLong(c.getColumnIndex(AppEvents.HISTORY_OBSERVED)); + + activeTimestamps.add(timestamp); + + String state = c.getString(c.getColumnIndex(AppEvents.HISTORY_STATE)); + activeStates.add(state); + } + + c.close(); + + String lastState = AppEvents.STATE_UNKNOWN; + + String lastWhere = AppEvents.HISTORY_OBSERVED + " < ?"; + String[] lastArgs = { "" + start }; + + c = generator.mDatabase.query(AppEvents.TABLE_HISTORY, null, lastWhere, lastArgs, null, null, AppEvents.HISTORY_OBSERVED + " DESC"); + + if (c.moveToNext()) { + lastState = c.getString(c.getColumnIndex(AppEvents.HISTORY_STATE)); + } + + if (activeStates.size() > 0) { + long firstTimestamp = activeTimestamps.get(0); + long firstState = activeTimestamps.get(0); + + View startView = new View(context); + + if (Build.VERSION.SDK_INT > Build.VERSION_CODES.KITKAT) { + if (AppEvents.STATE_UNKNOWN.equals(lastState)) { + + } else if (AppEvents.STATE_ON.equals(lastState)) { + startView.setBackgroundColor(0xff4CAF50); + } else if (AppEvents.STATE_OFF.equals(lastState)) { + startView.setBackgroundColor(0xff263238); + } else if (AppEvents.STATE_DOZE.equals(lastState)) { + startView.setBackgroundColor(0xff1b5e20); + } else if (AppEvents.STATE_DOZE_SUSPEND.equals(lastState)) { + startView.setBackgroundColor(0xff1b5e20); + } + } else { + if (AppEvents.STATE_UNKNOWN.equals(lastState)) { + + } else if (AppEvents.STATE_ON.equals(lastState)) { + startView.setBackgroundColor(0xff4CAF50); + } else if (AppEvents.STATE_OFF.equals(lastState)) { + startView.setBackgroundColor(0xff263238); + } + } + + LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(0, ViewGroup.LayoutParams.MATCH_PARENT, firstTimestamp - start); + startView.setLayoutParams(params); + + timeline.addView(startView); + + long now = System.currentTimeMillis(); + + if (activeStates.size() == 1) { + View v = new View(context); + + if (Build.VERSION.SDK_INT > Build.VERSION_CODES.KITKAT) { + if (AppEvents.STATE_ON.equals(firstState)) { + v.setBackgroundColor(0xff4CAF50); + } else if (AppEvents.STATE_OFF.equals(firstState)) { + v.setBackgroundColor(0xff263238); + } else if (AppEvents.STATE_DOZE.equals(firstState)) { + v.setBackgroundColor(0xff3f51b5); + } else if (AppEvents.STATE_DOZE_SUSPEND.equals(firstState)) { + v.setBackgroundColor(0xff3f51b5); + } + } else { + if (AppEvents.STATE_ON.equals(firstState)) { + v.setBackgroundColor(0xff4CAF50); + } else if (AppEvents.STATE_OFF.equals(firstState)) { + v.setBackgroundColor(0xff263238); + } + } + + if (end > System.currentTimeMillis()) { + params = new LinearLayout.LayoutParams(0, ViewGroup.LayoutParams.MATCH_PARENT, now - firstTimestamp); + v.setLayoutParams(params); + } else { + params = new LinearLayout.LayoutParams(0, ViewGroup.LayoutParams.MATCH_PARENT, end - firstTimestamp); + v.setLayoutParams(params); + } + + timeline.addView(v); + } else { + for (int i = 1; i < activeStates.size(); i++) { + long currentTimestamp = activeTimestamps.get(i); + + long priorTimestamp = activeTimestamps.get(i - 1); + String priorState = activeStates.get(i - 1); + + View v = new View(context); + + if (Build.VERSION.SDK_INT > Build.VERSION_CODES.KITKAT) { + if (AppEvents.STATE_ON.equals(priorState)) { + v.setBackgroundColor(0xff4CAF50); + } else if (AppEvents.STATE_OFF.equals(priorState)) { + v.setBackgroundColor(0xff263238); + } else if (AppEvents.STATE_DOZE.equals(priorState)) { + v.setBackgroundColor(0xff3f51b5); + } else if (AppEvents.STATE_DOZE_SUSPEND.equals(priorState)) { + v.setBackgroundColor(0xff3f51b5); + } + } else { + if (AppEvents.STATE_ON.equals(priorState)) { + v.setBackgroundColor(0xff4CAF50); + } else if (AppEvents.STATE_OFF.equals(priorState)) { + v.setBackgroundColor(0xff263238); + } + } + + params = new LinearLayout.LayoutParams(0, ViewGroup.LayoutParams.MATCH_PARENT, currentTimestamp - priorTimestamp); + v.setLayoutParams(params); + + timeline.addView(v); + } + + long finalTimestamp = activeTimestamps.get(activeTimestamps.size() - 1); + String finalState = activeStates.get(activeStates.size() - 1); + + View v = new View(context); + + if (AppEvents.STATE_ON.equals(finalState)) { + v.setBackgroundColor(0xff4CAF50); + } else if (AppEvents.STATE_OFF.equals(finalState)) { + v.setBackgroundColor(0xff263238); + } else if (AppEvents.STATE_DOZE.equals(finalState)) { + v.setBackgroundColor(0xff3f51b5); + } else if (AppEvents.STATE_DOZE_SUSPEND.equals(finalState)) { + v.setBackgroundColor(0xff3f51b5); + } + + if (end > now) { + params = new LinearLayout.LayoutParams(0, ViewGroup.LayoutParams.MATCH_PARENT, now - finalTimestamp); + v.setLayoutParams(params); + } else { + params = new LinearLayout.LayoutParams(0, ViewGroup.LayoutParams.MATCH_PARENT, end - finalTimestamp); + v.setLayoutParams(params); + } + + timeline.addView(v); + } + + if (end > now) { + View v = new View(context); + + params = new LinearLayout.LayoutParams(0, ViewGroup.LayoutParams.MATCH_PARENT, end - now); + v.setLayoutParams(params); + + timeline.addView(v); + } + } else { + + } + } + +*/ + public static View fetchView(ViewGroup parent) + { + return LayoutInflater.from(parent.getContext()).inflate(R.layout.card_generator_generic, parent, false); + } + + @Override + public List fetchPayloads() { + return new ArrayList<>(); + } + + public static long latestPointGenerated(Context context) { + long timestamp = 0; + + AppEvent me = AppEvent.getInstance(context); + + Cursor c = me.mDatabase.query(AppEvent.TABLE_HISTORY, null, null, null, null, null, AppEvent.HISTORY_OBSERVED + " DESC"); + + if (c.moveToNext()) { + timestamp = c.getLong(c.getColumnIndex(AppEvent.HISTORY_OBSERVED)); + } + + c.close(); + + return timestamp; + } + + public Cursor queryHistory(String[] cols, String where, String[] args, String orderBy) { + return this.mDatabase.query(AppEvent.TABLE_HISTORY, cols, where, args, null, null, orderBy); + } + + public boolean logEvent(String eventName, Map eventDetails) { + try { + long now = System.currentTimeMillis(); + + ContentValues values = new ContentValues(); + values.put(AppEvent.HISTORY_OBSERVED, now); + values.put(AppEvent.HISTORY_EVENT_NAME, eventName); + + Bundle detailsBundle = new Bundle(); + JSONObject detailsJson = new JSONObject(); + + for (String key : eventDetails.keySet()) { + Object value = eventDetails.get(key); + + if (value instanceof Double) { + Double doubleValue = ((Double) value); + + detailsBundle.putDouble(key, doubleValue); + detailsJson.put(key, doubleValue.doubleValue()); + } else if (value instanceof Float) { + Float floatValue = ((Float) value); + + detailsBundle.putDouble(key, floatValue.doubleValue()); + detailsJson.put(key, floatValue.doubleValue()); + } else if (value instanceof Long) { + Long longValue = ((Long) value); + + detailsBundle.putLong(key, longValue.longValue()); + detailsJson.put(key, longValue.longValue()); + } else if (value instanceof Integer) { + Integer intValue = ((Integer) value); + + detailsBundle.putLong(key, intValue.longValue()); + detailsJson.put(key, intValue.longValue()); + } else if (value instanceof String) { + detailsBundle.putString(key, value.toString()); + detailsJson.put(key, value.toString()); + } else if (value instanceof Boolean) { + detailsBundle.putBoolean(key, ((Boolean) value).booleanValue()); + detailsJson.put(key, ((Boolean) value).booleanValue()); + } else { + detailsBundle.putString(key, "Unknown Class: " + value.getClass().getCanonicalName()); + detailsJson.put(key, "Unknown Class: " + value.getClass().getCanonicalName()); + } + } + + values.put(AppEvent.HISTORY_EVENT_DETAILS, detailsJson.toString(2)); + + this.mDatabase.insert(AppEvent.TABLE_HISTORY, null, values); + + Bundle update = new Bundle(); + update.putLong(AppEvent.HISTORY_OBSERVED, now); + update.putString(AppEvent.HISTORY_EVENT_NAME, values.getAsString(AppEvent.HISTORY_EVENT_NAME)); + update.putBundle(AppEvent.HISTORY_EVENT_DETAILS, detailsBundle); + + Generators.getInstance(this.mContext).notifyGeneratorUpdated(AppEvent.GENERATOR_IDENTIFIER, update); + + return true; + } catch (JSONException e) { + e.printStackTrace(); + } + + return false; + } +} diff --git a/src/com/audacious_software/passive_data_kit/transmitters/HttpTransmitter.java b/src/com/audacious_software/passive_data_kit/transmitters/HttpTransmitter.java index 863d30e..1d9a2cb 100755 --- a/src/com/audacious_software/passive_data_kit/transmitters/HttpTransmitter.java +++ b/src/com/audacious_software/passive_data_kit/transmitters/HttpTransmitter.java @@ -424,10 +424,9 @@ public long transmittedSize() { } @Override - public void onGeneratorUpdated(String identifier, Bundle data) { + public void onGeneratorUpdated(String identifier, long timestamp, Bundle data) { if (data.keySet().size() > 1) { // Only transmit non-empty bundles... - double now = (double) System.currentTimeMillis(); - now = now / 1000; // Convert to seconds... + timestamp = timestamp / 1000; // Convert to seconds... Generators generators = Generators.getInstance(this.mContext); @@ -438,7 +437,7 @@ public void onGeneratorUpdated(String identifier, Bundle data) { } metadata.putString(Generator.IDENTIFIER, identifier); - metadata.putDouble(Generator.TIMESTAMP, now); + metadata.putDouble(Generator.TIMESTAMP, timestamp); metadata.putString(Generator.GENERATOR, generators.getGeneratorFullName(identifier)); metadata.putString(Generator.SOURCE, generators.getSource()); metadata.putString(Generator.SOURCE, this.mUserId);