ViewLog.java 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544
  1. package de.tudarmstadt.informatik.hostage.ui;
  2. import java.io.File;
  3. import java.io.FileOutputStream;
  4. import java.text.SimpleDateFormat;
  5. import java.util.ArrayList;
  6. import java.util.Arrays;
  7. import java.util.Calendar;
  8. import java.util.Date;
  9. import java.util.Locale;
  10. import android.annotation.SuppressLint;
  11. import android.app.Activity;
  12. import android.app.AlertDialog;
  13. import android.app.DatePickerDialog;
  14. import android.app.Dialog;
  15. import android.app.NotificationManager;
  16. import android.app.PendingIntent;
  17. import android.content.Context;
  18. import android.content.DialogInterface;
  19. import android.content.Intent;
  20. import android.content.SharedPreferences;
  21. import android.content.SharedPreferences.Editor;
  22. import android.os.Build;
  23. import android.os.Bundle;
  24. import android.os.Environment;
  25. import android.preference.PreferenceManager;
  26. import android.support.v4.app.NotificationCompat;
  27. import android.support.v4.app.TaskStackBuilder;
  28. import android.util.Log;
  29. import android.view.Gravity;
  30. import android.view.Menu;
  31. import android.view.MenuItem;
  32. import android.view.View;
  33. import android.widget.DatePicker;
  34. import android.widget.TableLayout;
  35. import android.widget.TableRow;
  36. import android.widget.TextView;
  37. import android.widget.TimePicker;
  38. import android.widget.Toast;
  39. import de.tudarmstadt.informatik.hostage.R;
  40. import de.tudarmstadt.informatik.hostage.commons.HelperUtils;
  41. import de.tudarmstadt.informatik.hostage.logging.Record;
  42. import de.tudarmstadt.informatik.hostage.logging.formatter.TraCINgFormatter;
  43. /**
  44. * ViewLog shows Statistics about the recorded Attacks. It also offers the user
  45. * several options to perform actions with the database. This includes:<br>
  46. * - show records,<br>
  47. * - export records,<br>
  48. * - upload records,<br>
  49. * - and delete records.
  50. *
  51. * @author Lars Pandikow
  52. */
  53. public class ViewLog extends Activity {
  54. private final SimpleDateFormat sdf = new SimpleDateFormat(
  55. "MMM dd,yyyy HH:mm", Locale.US);
  56. private SharedPreferences pref;
  57. private Editor editor;
  58. /**
  59. * Creates a Dialog that lets the user decide which criteria he want to use
  60. * to delete records. Then calls the corresponding method.<br>
  61. * The possible criteria are coded in /res/values/arrays.xml To add a
  62. * criteria add a String to the array and extend the switch statement.
  63. *
  64. * @param view
  65. * View elements which triggers the method call.
  66. * @see ViewLog#deleteByBSSID()
  67. * @see ViewLog#deleteByDate()
  68. * @see ViewLog#deleteAll()
  69. */
  70. public void deleteLog(View view) {
  71. AlertDialog.Builder builder = new AlertDialog.Builder(this);
  72. builder.setTitle(R.string.gui_delete_dialog_title);
  73. builder.setItems(R.array.delete_criteria,
  74. new DialogInterface.OnClickListener() {
  75. @Override
  76. public void onClick(DialogInterface dialog, int position) {
  77. switch (position) {
  78. case 0:
  79. deleteByBSSID();
  80. break;
  81. case 1:
  82. deleteByDate();
  83. break;
  84. case 2:
  85. deleteAll();
  86. }
  87. }
  88. });
  89. builder.create();
  90. builder.show();
  91. }
  92. /**
  93. * Creates a Dialog to choose export format. Then calls
  94. * {@link ViewLog#exportDatabase(int)}
  95. *
  96. * @param view
  97. * View elements which triggers the method call.
  98. */
  99. public void exportDatabase(View view) {
  100. AlertDialog.Builder builder = new AlertDialog.Builder(this);
  101. builder.setTitle(R.string.gui_export_dialog_title);
  102. builder.setItems(R.array.format, new DialogInterface.OnClickListener() {
  103. @Override
  104. public void onClick(DialogInterface dialog, int position) {
  105. exportDatabase(position);
  106. }
  107. });
  108. builder.create();
  109. builder.show();
  110. }
  111. @Override
  112. public boolean onCreateOptionsMenu(Menu menu) {
  113. getMenuInflater().inflate(R.menu.main, menu);
  114. return true;
  115. }
  116. @Override
  117. public boolean onOptionsItemSelected(MenuItem item) {
  118. // Handle item selection
  119. switch (item.getItemId()) {
  120. case R.id.action_settings:
  121. startActivity(new Intent(this, SettingsActivity.class));
  122. break;
  123. case R.id.action_about:
  124. startActivity(new Intent(this, AboutActivity.class));
  125. break;
  126. default:
  127. }
  128. return super.onOptionsItemSelected(item);
  129. }
  130. /**
  131. * Starts a ViewLogTable Activity.
  132. *
  133. * @param view
  134. * View elements which triggers the method call.
  135. * @see ViewLogTable
  136. */
  137. public void showLog(View view) {
  138. startActivity(new Intent(this, ViewLogTable.class));
  139. // TODO Delete
  140. }
  141. /**
  142. * Uploads a JSON Representation of each attack to a server, specified in
  143. * the preferences.<br>
  144. * The method only uploads information about attacks that have net been
  145. * uploaded yet.<br>
  146. * The local and remote IP of each record will be replaced with the external
  147. * IP of then device at the time of the upload. For the Upload it uses a
  148. * HttpPost with a HttpsClient, which does not validate any certificates.<br>
  149. * The Upload runs in its own Thread.<br>
  150. * While uploading the method also creates a notification to inform the user
  151. * about the status of the upload.<br>
  152. * <b>Only uploads Records with a external IP that is not null!
  153. *
  154. * @param view
  155. * View elements which triggers the method call.
  156. * @see de.tudarmstadt.informatik.hostage.net.MySSLSocketFactory
  157. */
  158. public void uploadDatabase(View view) {
  159. // Create a Notification
  160. final NotificationCompat.Builder builder;
  161. final NotificationManager mNotifyManager;
  162. final int lastUploadedAttackId = pref.getInt("LAST_UPLOADED_ATTACK_ID",
  163. -1);
  164. int currentAttackId = pref.getInt("ATTACK_ID_COUNTER", 0);
  165. // Check if there are new records to upload
  166. if (lastUploadedAttackId == currentAttackId - 1) {
  167. // Inform user that no upload is necessary
  168. Toast.makeText(this, "All data have already been uploaded.",
  169. Toast.LENGTH_SHORT).show();
  170. return;
  171. }
  172. // Build and show Upload Notification in notification bar
  173. builder = new NotificationCompat.Builder(this)
  174. .setContentTitle(this.getString(R.string.app_name))
  175. .setContentText("Upload in progress...")
  176. .setTicker("Uploading Data...")
  177. .setSmallIcon(R.drawable.ic_launcher)
  178. .setWhen(System.currentTimeMillis());
  179. TaskStackBuilder stackBuilder = TaskStackBuilder.create(this);
  180. stackBuilder.addParentStack(MainActivity.class);
  181. stackBuilder.addNextIntent(new Intent());
  182. PendingIntent resultPendingIntent = stackBuilder.getPendingIntent(0,
  183. PendingIntent.FLAG_UPDATE_CURRENT);
  184. builder.setContentIntent(resultPendingIntent);
  185. builder.setAutoCancel(true);
  186. builder.setOnlyAlertOnce(false);
  187. mNotifyManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
  188. mNotifyManager.notify(2, builder.build());
  189. final Context context = this;
  190. // Create a new Thread for upload
  191. new Thread(new Runnable() {
  192. @Override
  193. public void run() {
  194. // get RecordList
  195. ArrayList<Record> recordList = null;// logger.getRecordOfEachAttack(lastUploadedAttackId);
  196. final int progressMax = recordList.size();
  197. Log.i("SQLLogger", "Logs to upload: " + progressMax);
  198. int progressBarStatus = 0;
  199. int retry_counter = 0;
  200. while (progressBarStatus < progressMax) {
  201. Record record = recordList.get(progressBarStatus);
  202. // Only upload records with a saved external ip
  203. if (record.getExternalIP() != null) {
  204. retry_counter = 0;
  205. if (HelperUtils.uploadSingleRecord(context, record)) {
  206. // Update Notification progress bar
  207. progressBarStatus++;
  208. builder.setProgress(progressMax, progressBarStatus,
  209. false);
  210. // Update the progress bar
  211. mNotifyManager.notify(2, builder.build());
  212. } else {
  213. retry_counter++;
  214. if (retry_counter == 3) {
  215. retry_counter = 0;
  216. progressBarStatus++;
  217. }
  218. }
  219. }
  220. }
  221. if (progressBarStatus == progressMax) {
  222. // When the loop is finished, updates the notification
  223. builder.setContentText("Upload complete")
  224. .setTicker("Upload complete")
  225. // Removes the progress bar
  226. .setProgress(0, 0, false);
  227. mNotifyManager.notify(2, builder.build());
  228. }
  229. }
  230. }).start();
  231. editor.putInt("LAST_UPLOADED_ATTACK_ID", currentAttackId - 1);
  232. editor.commit();
  233. }
  234. /**
  235. * Shows a Dialog to confirm that the database should be cleared. If
  236. * confirmed {@link SQLLogger#clearData()} is called.
  237. *
  238. * @see ILogger#clearData()
  239. */
  240. private void deleteAll() {
  241. AlertDialog.Builder builder = new AlertDialog.Builder(this);
  242. builder.setMessage(R.string.gui_dialog_clear_database)
  243. .setPositiveButton(R.string.gui_clear,
  244. new DialogInterface.OnClickListener() {
  245. @Override
  246. @SuppressLint("NewApi")
  247. public void onClick(DialogInterface dialog, int id) {
  248. // Clear all Data
  249. // logger.clearData();
  250. editor.putInt("ATTACK_ID_COUNTER", 0);
  251. editor.putInt("LAST_UPLOADED_ATTACK_ID", -1);
  252. editor.commit();
  253. Toast.makeText(getApplicationContext(),
  254. "Database cleared!", Toast.LENGTH_SHORT)
  255. .show();
  256. // Recreate the activity
  257. if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {
  258. recreate();
  259. } else {
  260. Intent intent = getIntent();
  261. finish();
  262. startActivity(intent);
  263. }
  264. }
  265. })
  266. .setNegativeButton(R.string.gui_cancel,
  267. new DialogInterface.OnClickListener() {
  268. @Override
  269. public void onClick(DialogInterface dialog, int id) {
  270. // User cancelled the dialog
  271. }
  272. });
  273. // Create the AlertDialog object
  274. builder.create();
  275. builder.show();
  276. }
  277. /**
  278. * Shows a List with all recorded BSSIDs. If a BSSID is selected the method
  279. * calls {@link ILogger#deleteByBSSID(String)} to delete all records with
  280. * the chosen BSSID
  281. *
  282. * @see ILogger#deleteByBSSID
  283. */
  284. private void deleteByBSSID() {
  285. AlertDialog.Builder builder = new AlertDialog.Builder(this);
  286. final String[] bssidArray = null;// logger.getAllBSSIDS();
  287. final String[] strings = new String[bssidArray.length];
  288. for (int i = 0; i < bssidArray.length; i++) {
  289. strings[i] = bssidArray[i] + " (" // + logger.getSSID(bssidArray[i])
  290. + ")";
  291. }
  292. builder.setTitle(R.string.gui_delete_dialog_title);
  293. builder.setItems(strings, new DialogInterface.OnClickListener() {
  294. @Override
  295. @SuppressLint("NewApi")
  296. public void onClick(DialogInterface dialog, int position) {
  297. // logger.deleteByBSSID(bssidArray[position]);
  298. Toast.makeText(
  299. getApplicationContext(),
  300. "All entries with bssid '" + bssidArray[position]
  301. + "' deleted.", Toast.LENGTH_SHORT).show();
  302. if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {
  303. recreate();
  304. } else {
  305. Intent intent = getIntent();
  306. finish();
  307. startActivity(intent);
  308. }
  309. }
  310. });
  311. builder.create();
  312. builder.show();
  313. }
  314. /**
  315. * Creates a DatePicking Dialog where the user can choose a date. Afterwards
  316. * {@link ViewLog#deleteByDate(int, int, int)} is called.
  317. */
  318. private void deleteByDate() {
  319. showDialog(0);
  320. }
  321. /**
  322. * Shows a Dialog with the given date and ask him to confirm deleting
  323. * records that are older than the shown date. When the user confirms
  324. * {@link ILogger#deleteByDate(long)} is called with a long representation
  325. * of the Date. If the user cancels the Dialog nothing happens and the
  326. * dialog disappears.
  327. *
  328. * @param year
  329. * @param monthOfYear
  330. * @param dayOfMonth
  331. */
  332. private void deleteByDate(int year, int monthOfYear, int dayOfMonth) {
  333. TimePicker timePicker = new TimePicker(this);
  334. final Calendar calendar = Calendar.getInstance();
  335. calendar.set(year, monthOfYear, dayOfMonth,
  336. timePicker.getCurrentHour(), timePicker.getCurrentMinute(), 0);
  337. AlertDialog.Builder builder = new AlertDialog.Builder(this);
  338. builder.setTitle(R.string.gui_dialog_clear_database_date)
  339. .setMessage(sdf.format(calendar.getTime()))
  340. .setPositiveButton(R.string.gui_delete,
  341. new DialogInterface.OnClickListener() {
  342. @Override
  343. @SuppressLint("NewApi")
  344. public void onClick(DialogInterface dialog, int id) {
  345. long time = calendar.getTimeInMillis();
  346. // Delete Data
  347. // logger.deleteByDate(time);
  348. Toast.makeText(getApplicationContext(),
  349. "Data sets deleted!",
  350. Toast.LENGTH_SHORT).show();
  351. // Recreate the activity
  352. if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {
  353. recreate();
  354. } else {
  355. Intent intent = getIntent();
  356. finish();
  357. startActivity(intent);
  358. }
  359. }
  360. })
  361. .setNegativeButton(R.string.gui_cancel,
  362. new DialogInterface.OnClickListener() {
  363. @Override
  364. public void onClick(DialogInterface dialog, int id) {
  365. // User cancelled the dialog
  366. }
  367. });
  368. // Create the AlertDialog object
  369. builder.create();
  370. builder.show();
  371. }
  372. /**
  373. * Exports all records in a given format. Before exporting checks export
  374. * location from preferences.
  375. *
  376. * @param format
  377. * Integer coded export format
  378. * @see Record#toString(int)
  379. */
  380. private void exportDatabase(int format) {
  381. try {
  382. FileOutputStream log;
  383. String filename = "hostage_" + format + "_"
  384. + System.currentTimeMillis() + ".log";
  385. boolean externalStorage = pref.getBoolean("pref_external_storage",
  386. false);
  387. String externalLocation = pref.getString("pref_external_location",
  388. "");
  389. if (externalStorage) {
  390. String root = Environment.getExternalStorageDirectory()
  391. .toString();
  392. if (root != null && HelperUtils.isExternalStorageWritable()) {
  393. File dir = new File(root + externalLocation);
  394. dir.mkdirs();
  395. File file = new File(dir, filename);
  396. log = new FileOutputStream(file);
  397. } else {
  398. Toast.makeText(this, "Could not write to SD Card",
  399. Toast.LENGTH_SHORT).show();
  400. return;
  401. }
  402. } else {
  403. log = this.openFileOutput(
  404. "hostage_" + format + "_" + System.currentTimeMillis()
  405. + ".log", Context.MODE_PRIVATE);
  406. }
  407. ArrayList<Record> records = null;// logger.getAllRecords();
  408. for (Record record : records) {
  409. log.write((record.toString((format == 1) ? TraCINgFormatter
  410. .getInstance() : null)).getBytes());
  411. }
  412. log.flush();
  413. log.close();
  414. Toast.makeText(
  415. this,
  416. externalStorage ? filename + " saved on external memory! "
  417. + externalLocation : filename
  418. + " saved on internal memory!", Toast.LENGTH_LONG)
  419. .show();
  420. } catch (Exception e) {
  421. Toast.makeText(this, "Could not write to SD Card",
  422. Toast.LENGTH_SHORT).show();
  423. e.printStackTrace();
  424. }
  425. }
  426. /**
  427. * Initializes the Statistics. Creates a table row for every protocol and
  428. * checks the dabase for the attack count. Calls
  429. * {@link ViewLog#setFirstAndLastAttack()} to set the TextViews.
  430. *
  431. * @see ILogger#getAttackCount()
  432. * @see ILogger#getAttackPerProtocolCount(String)
  433. */
  434. private void initStatistic() {
  435. TableLayout table = (TableLayout) findViewById(R.id.layoutContainer);
  436. ArrayList<String> protocols = new ArrayList<String>();
  437. protocols.add("Total");
  438. protocols.addAll(Arrays.asList(getResources().getStringArray(
  439. R.array.protocols)));
  440. for (String protocol : protocols) {
  441. TableRow row = new TableRow(this);
  442. TextView protocolName = new TextView(this);
  443. protocolName.setBackgroundResource(R.color.dark_grey);
  444. protocolName.setText(protocol);
  445. protocolName.setTextAppearance(this,
  446. android.R.style.TextAppearance_Medium);
  447. protocolName.setPadding(6, 0, 0, 0);
  448. row.addView(protocolName);
  449. TextView value = new TextView(this);
  450. value.setBackgroundResource(R.color.light_grey);
  451. value.setTextAppearance(this, android.R.style.TextAppearance_Medium);
  452. value.setGravity(Gravity.RIGHT);
  453. value.setPadding(3, 0, 3, 0);
  454. row.addView(value);
  455. if (protocol.equals("Total")) {
  456. value.setText(""); // + logger.getAttackCount());
  457. } else {
  458. value.setText("");// +
  459. // logger.getAttackPerProtocolCount(protocol));
  460. }
  461. table.addView(row);
  462. }
  463. setFirstAndLastAttack();
  464. }
  465. /**
  466. * Sets the TextViews for first and last attack.
  467. *
  468. * @see ILogger#getSmallestAttackId()
  469. * @see ILogger#getHighestAttackId()
  470. * @see ILogger#getRecordOfAttackId(long)
  471. */
  472. private void setFirstAndLastAttack() {
  473. Record firstAttack = null;// logger.getRecordOfAttackId(logger.getSmallestAttackId());
  474. Record lastAttack = null;// logger.getRecordOfAttackId(logger.getHighestAttackId());
  475. if (firstAttack != null) {
  476. Date resultdate = new Date(firstAttack.getTimestamp());
  477. TextView text = (TextView) findViewById(R.id.textFirstAttackValue);
  478. text.setText(sdf.format(resultdate));
  479. text = (TextView) findViewById(R.id.textLastAttackValue);
  480. resultdate = new Date(lastAttack.getTimestamp());
  481. text.setText(sdf.format(resultdate));
  482. } else {
  483. TextView text = (TextView) findViewById(R.id.textFirstAttackValue);
  484. text.setText("-");
  485. text = (TextView) findViewById(R.id.textLastAttackValue);
  486. text.setText("-");
  487. }
  488. }
  489. @Override
  490. protected void onCreate(Bundle savedInstanceState) {
  491. super.onCreate(savedInstanceState);
  492. setContentView(R.layout.activity_viewlog);
  493. pref = PreferenceManager.getDefaultSharedPreferences(this);
  494. editor = pref.edit();
  495. initStatistic();
  496. }
  497. @Override
  498. protected Dialog onCreateDialog(int id) {
  499. switch (id) {
  500. case 0:
  501. final Calendar cal = Calendar.getInstance();
  502. int pYear = cal.get(Calendar.YEAR);
  503. int pMonth = cal.get(Calendar.MONTH);
  504. int pDay = cal.get(Calendar.DAY_OF_MONTH);
  505. return new DatePickerDialog(this,
  506. new DatePickerDialog.OnDateSetListener() {
  507. @Override
  508. public void onDateSet(DatePicker view, int year,
  509. int monthOfYear, int dayOfMonth) {
  510. deleteByDate(year, monthOfYear, dayOfMonth);
  511. }
  512. }, pYear, pMonth, pDay);
  513. }
  514. return null;
  515. }
  516. }