package fuzion24.device.vulnerability.test.ui; import android.Manifest; import android.content.Intent; import android.content.pm.PackageManager; import android.content.res.Configuration; import android.graphics.Color; import android.net.Uri; import android.os.Bundle; import android.support.design.widget.CoordinatorLayout; import android.support.design.widget.FloatingActionButton; import android.support.design.widget.Snackbar; import android.support.v4.app.ActivityCompat; import android.support.v4.content.ContextCompat; import android.support.v7.app.AppCompatActivity; import android.support.v7.widget.LinearLayoutManager; import android.support.v7.widget.RecyclerView; import android.support.v7.widget.Toolbar; import android.util.Log; import android.view.Menu; import android.view.MenuItem; import android.view.View; import android.widget.TextView; import com.afollestad.materialdialogs.DialogAction; import com.afollestad.materialdialogs.MaterialDialog; import com.evernote.android.job.JobManager; import com.evernote.android.job.JobRequest; import com.nowsecure.android.vts.R; import org.json.JSONException; import org.json.JSONObject; import java.util.ArrayList; import java.util.List; import java.util.concurrent.TimeUnit; import fuzion24.device.vulnerability.test.ResultsCallback; import fuzion24.device.vulnerability.test.ShareViaGoogleForm; import fuzion24.device.vulnerability.test.VulnerabilityTestResult; import fuzion24.device.vulnerability.test.VulnerabilityTestRunner; import fuzion24.device.vulnerability.test.adapter.RecyclerAdapter; import fuzion24.device.vulnerability.test.job.AndroidVTSJobCreator; import fuzion24.device.vulnerability.test.job.CheckForUpdatesJob; import fuzion24.device.vulnerability.util.DeviceInfo; import fuzion24.device.vulnerability.util.SharedPreferencesUtils; import fuzion24.device.vulnerability.vulnerabilities.VulnerabilityResultSerialzier; public class MainActivity extends AppCompatActivity { private static final String SERIALIZABLE_RESULTS = "SERIALIZABLE_RESULTS"; private static final String TAG = "VULN_TEST"; private static final int CREATE_CHECK_FOR_UPDATES_JOB = 1; private static final int CHECK_FOR_UPDATES = 2; private DeviceInfo devInfo; private ArrayList<VulnerabilityTestResult> testResults; private TextView emptyView; private CoordinatorLayout coordinatorLayout; private RecyclerAdapter recyclerAdapter; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar); toolbar.setTitleTextColor(Color.WHITE); setSupportActionBar(toolbar); getSupportActionBar().setTitle(R.string.app_name); if (savedInstanceState != null && savedInstanceState.containsKey(SERIALIZABLE_RESULTS)) { testResults = (ArrayList<VulnerabilityTestResult>) savedInstanceState.getSerializable(SERIALIZABLE_RESULTS); } else { testResults = new ArrayList<>(); } RecyclerView recyclerView = (RecyclerView) findViewById(R.id.recyclerView); coordinatorLayout = (CoordinatorLayout) findViewById(R.id.coordinatorLayout); emptyView = (TextView) findViewById(R.id.emptyView); recyclerAdapter = new RecyclerAdapter(MainActivity.this, testResults); recyclerView.setLayoutManager(new LinearLayoutManager(MainActivity.this)); recyclerView.setAdapter(recyclerAdapter); final TextView tvKernelVersion = (TextView) findViewById(R.id.kernelVersion); final TextView tvBuildFingerPrint = (TextView) findViewById(R.id.buildFingerPrint); final TextView tvBuildID = (TextView) findViewById(R.id.buildID); final TextView tvBuildBrand = (TextView) findViewById(R.id.buildBrand); final TextView tvBuildManuf = (TextView) findViewById(R.id.buildManufacturer); final TextView tvBuildModel = (TextView) findViewById(R.id.buildModel); final TextView tvBuildRelease = (TextView) findViewById(R.id.buildRelease); final TextView tvBuildSDK = (TextView) findViewById(R.id.buildSDK); final TextView tvBuildABIList = (TextView) findViewById(R.id.buildABIList); devInfo = DeviceInfo.getDeviceInfo(); tvBuildFingerPrint.setText(devInfo.getBuildFingerPrint()); tvBuildID.setText(devInfo.getBuildID()); tvKernelVersion.setText(devInfo.getKernelVersion()); tvBuildBrand.setText(devInfo.getBuildBrand()); tvBuildManuf.setText(devInfo.getBuildManufacturer()); tvBuildModel.setText(devInfo.getBuildModel()); tvBuildRelease.setText(devInfo.getBuildRelease()); tvBuildSDK.setText(devInfo.getBuildSDK()); StringBuilder sb = new StringBuilder(); for (String s : devInfo.getSupportedABIS()) { sb.append(s); sb.append(" "); } tvBuildABIList.setText(sb.toString()); FloatingActionButton fabStart = (FloatingActionButton) findViewById(R.id.fabStart); fabStart.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { runTestsSuit(); } }); // Creates the jobs to check for updates. JobManager.instance().cancelAllForTag(CheckForUpdatesJob.TAG); if (ContextCompat.checkSelfPermission(this, Manifest.permission.WRITE_EXTERNAL_STORAGE) == PackageManager.PERMISSION_GRANTED) { createCheckForUpdatesJob(); } else { ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE}, CREATE_CHECK_FOR_UPDATES_JOB); } } @Override public boolean onCreateOptionsMenu(Menu menu) { getMenuInflater().inflate(R.menu.main_menu, menu); menu.getItem(2).setChecked(SharedPreferencesUtils.isAutomaticSharingEnable(MainActivity.this)); return true; } @Override public boolean onOptionsItemSelected(MenuItem item) { int itemId = item.getItemId(); switch (itemId) { case R.id.menu_export_results: case R.id.menu_share_results: if (testResults == null || testResults.isEmpty()) { Snackbar.make(coordinatorLayout, R.string.run_tests, Snackbar.LENGTH_LONG).setAction(R.string.start, new View.OnClickListener() { @Override public void onClick(View v) { runTestsSuit(); } }).show(); return true; } Intent intent = null; try { JSONObject json = VulnerabilityResultSerialzier.serializeResultsToJson(testResults, devInfo); if (itemId == R.id.menu_export_results) { intent = new Intent(Intent.ACTION_SEND); intent.setType("text/plain"); intent.putExtra(Intent.EXTRA_SUBJECT, "Android VTS Results"); intent.putExtra(Intent.EXTRA_TEXT, json.toString(4)); } else if (itemId == R.id.menu_share_results) { Uri formUri = new ShareViaGoogleForm(json).buildUri(); intent = new Intent(Intent.ACTION_VIEW, formUri); } } catch (JSONException e) { Log.d(TAG, "Json exception: " + e.getMessage()); Snackbar.make(coordinatorLayout, R.string.error_exporting_results, Snackbar.LENGTH_LONG).show(); } if (intent != null) { startActivity(Intent.createChooser(intent, getString(R.string.share_results_via))); } return true; case R.id.menu_automatic_share_results: new MaterialDialog.Builder(this) .title(R.string.dialog_automatic_sharing_title) .content(R.string.dialog_automatic_sharing_description) .positiveText(R.string.global_sharing) .negativeText(R.string.global_notsharing) .onPositive(new MaterialDialog.SingleButtonCallback() { @Override public void onClick(MaterialDialog materialDialog, DialogAction dialogAction) { SharedPreferencesUtils.setIsAutomaticSharingEnable(MainActivity.this, true); } }) .onNegative(new MaterialDialog.SingleButtonCallback() { @Override public void onClick(MaterialDialog materialDialog, DialogAction dialogAction) { SharedPreferencesUtils.setIsAutomaticSharingEnable(MainActivity.this, false); } }) .onAny(new MaterialDialog.SingleButtonCallback() { @Override public void onClick(MaterialDialog materialDialog, DialogAction dialogAction) { invalidateOptionsMenu(); } }) .show(); return true; case R.id.menu_check_updates: if (ContextCompat.checkSelfPermission(this, Manifest.permission.WRITE_EXTERNAL_STORAGE) == PackageManager.PERMISSION_GRANTED) { checkForUpdates(); } else { ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE}, CHECK_FOR_UPDATES); } return true; case R.id.menu_opensource_licences: Intent mainIntent = new Intent(MainActivity.this, OpenSourceLicencesActivity.class); startActivity(mainIntent); return true; default: return super.onOptionsItemSelected(item); } } private void runTestsSuit() { new VulnerabilityTestRunner(MainActivity.this, true, new ResultsCallback() { @Override public void finished(final List<VulnerabilityTestResult> results) { Log.d(TAG, "Device Vulnerability callback, finished"); testResults.clear(); testResults.addAll(results); emptyView.setVisibility(View.GONE); recyclerAdapter.updateResults(results); showScanResume(results); } }).execute(); } private void showScanResume(final List<VulnerabilityTestResult> results) { int numberOfFailed = 0; for (VulnerabilityTestResult result : results) { if (result.getException() != null) { continue; } if (result.isVulnerable()) { numberOfFailed++; } } MaterialDialog.Builder dialogBuilder = new MaterialDialog.Builder(this) .title(R.string.scan_details) .customView(R.layout.dialog_scan_details_layout, true) .positiveText(R.string.dismiss) .onAny(new MaterialDialog.SingleButtonCallback() { @Override public void onClick(MaterialDialog materialDialog, DialogAction dialogAction) { if (SharedPreferencesUtils.isAutomaticSharingEnable(MainActivity.this)) { try { JSONObject json = VulnerabilityResultSerialzier.serializeResultsToJson(testResults, devInfo); Uri formUri = new ShareViaGoogleForm(json).buildUri(); Intent intent = new Intent(Intent.ACTION_VIEW, formUri); startActivity(Intent.createChooser(intent, getString(R.string.share_results_via))); } catch (JSONException exception) { exception.printStackTrace(); } } } }); if (numberOfFailed > 0) { dialogBuilder.negativeText(R.string.my_device_is_vulnerable_info) .onNegative(new MaterialDialog.SingleButtonCallback() { @Override public void onClick(MaterialDialog materialDialog, DialogAction dialogAction) { Intent browserIntent = new Intent(Intent.ACTION_VIEW, Uri.parse("https://www.nowsecure.com/blog/2015/11/18/my-device-is-vulnerable-now-what/")); startActivity(browserIntent); } }); } View view = dialogBuilder.show().getCustomView(); TextView state = (TextView) view.findViewById(R.id.scan_details_device_state); if (numberOfFailed > 0) { state.setText(R.string.scan_details_vulnerable_message); state.setTextColor(getResources().getColor(R.color.red)); } else { state.setText(R.string.scan_details_secure_message); state.setTextColor(getResources().getColor(R.color.green)); } TextView numberOfTests = (TextView) view.findViewById(R.id.scan_details_number_tests); numberOfTests.setText(String.valueOf(results.size())); TextView numberOfFailedTests = (TextView) view.findViewById(R.id.scan_details_failed_tests); numberOfFailedTests.setText(String.valueOf(numberOfFailed)); } private void createCheckForUpdatesJob() { new JobRequest.Builder(CheckForUpdatesJob.TAG) .setPeriodic(TimeUnit.DAYS.toMillis(7)) .setRequirementsEnforced(true) .setRequiredNetworkType(JobRequest.NetworkType.CONNECTED) .setPersisted(true) .build() .schedule(); } private void checkForUpdates() { final MaterialDialog progressDialog = new MaterialDialog.Builder(this) .title(R.string.loading) .content(R.string.checking_updates) .cancelable(false) .progress(true, 0) .show(); CheckForUpdatesJob.checkForUpdates(this, progressDialog); } @Override public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) { switch (requestCode) { case CREATE_CHECK_FOR_UPDATES_JOB: if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) { createCheckForUpdatesJob(); } break; case CHECK_FOR_UPDATES: if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) { checkForUpdates(); } break; } } @Override public void onConfigurationChanged(Configuration newConfig) { super.onConfigurationChanged(newConfig); } @Override protected void onSaveInstanceState(final Bundle outState) { outState.putSerializable(SERIALIZABLE_RESULTS, testResults); } }