org.csploit.android.MainActivity.java Source code

Java tutorial

Introduction

Here is the source code for org.csploit.android.MainActivity.java

Source

/*
 * This file is part of the dSploit.
 *
 * Copyleft of Simone Margaritelli aka evilsocket <evilsocket@gmail.com>
 *
 * dSploit is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * dSploit is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with dSploit.  If not, see <http://www.gnu.org/licenses/>.
 */
package org.csploit.android;

import android.annotation.SuppressLint;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.SharedPreferences;
import android.graphics.Typeface;
import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
import android.support.v4.content.ContextCompat;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.view.ActionMode;
import android.text.Html;
import android.view.Gravity;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.View;
import android.view.ViewGroup;
import android.view.ViewGroup.LayoutParams;
import android.widget.AdapterView;
import android.widget.AdapterView.OnItemLongClickListener;
import android.widget.BaseAdapter;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.ListView;
import android.widget.RelativeLayout;
import android.widget.TextView;
import android.widget.Toast;

import org.csploit.android.core.Child;
import org.csploit.android.core.Client;
import org.csploit.android.core.CrashReporter;
import org.csploit.android.core.Logger;
import org.csploit.android.core.ManagedReceiver;
import org.csploit.android.core.MultiAttackService;
import org.csploit.android.core.Plugin;
import org.csploit.android.core.System;
import org.csploit.android.events.Event;
import org.csploit.android.gui.dialogs.AboutDialog;
import org.csploit.android.gui.dialogs.ConfirmDialog;
import org.csploit.android.gui.dialogs.ConfirmDialog.ConfirmDialogListener;
import org.csploit.android.gui.dialogs.ErrorDialog;
import org.csploit.android.gui.dialogs.FatalDialog;
import org.csploit.android.gui.dialogs.InputDialog;
import org.csploit.android.gui.dialogs.InputDialog.InputDialogListener;
import org.csploit.android.gui.dialogs.MultipleChoiceDialog;
import org.csploit.android.gui.dialogs.SpinnerDialog;
import org.csploit.android.gui.dialogs.SpinnerDialog.SpinnerDialogListener;
import org.csploit.android.helpers.ThreadHelper;
import org.csploit.android.net.Network;
import org.csploit.android.net.Target;
import org.csploit.android.plugins.ExploitFinder;
import org.csploit.android.plugins.Inspector;
import org.csploit.android.plugins.LoginCracker;
import org.csploit.android.plugins.PacketForger;
import org.csploit.android.plugins.PortScanner;
import org.csploit.android.plugins.RouterPwn;
import org.csploit.android.plugins.Sessions;
import org.csploit.android.plugins.Traceroute;
import org.csploit.android.plugins.mitm.MITM;
import org.csploit.android.services.Services;
import org.csploit.android.services.UpdateChecker;
import org.csploit.android.services.UpdateService;
import org.csploit.android.services.receivers.MsfRpcdServiceReceiver;
import org.csploit.android.services.receivers.NetworkRadarReceiver;
import org.csploit.android.update.CoreUpdate;
import org.csploit.android.update.MsfUpdate;
import org.csploit.android.update.RubyUpdate;
import org.csploit.android.update.Update;

import java.io.IOException;
import java.net.NoRouteToHostException;
import java.util.ArrayList;
import java.util.List;
import java.util.Observable;
import java.util.Observer;

import static org.csploit.android.services.UpdateChecker.UPDATE_AVAILABLE;
import static org.csploit.android.services.UpdateChecker.UPDATE_CHECKING;
import static org.csploit.android.services.UpdateChecker.UPDATE_NOT_AVAILABLE;

@SuppressLint("NewApi")
public class MainActivity extends AppCompatActivity {
    private String UPDATE_MESSAGE;
    private static final int WIFI_CONNECTION_REQUEST = 1012;
    private boolean isWifiAvailable = false;
    private TargetAdapter mTargetAdapter = null;
    private NetworkRadarReceiver mRadarReceiver = new NetworkRadarReceiver();
    private UpdateReceiver mUpdateReceiver = new UpdateReceiver();
    private WipeReceiver mWipeReceiver = new WipeReceiver();
    private MsfRpcdServiceReceiver mMsfReceiver = new MsfRpcdServiceReceiver();
    private Menu mMenu = null;
    private TextView mUpdateStatus = null;
    private Toast mToast = null;
    private long mLastBackPressTime = 0;
    private ActionMode mActionMode = null;
    private ListView lv;
    private boolean isRootMissing = false;

    private void createUpdateStatusText() {
        if (mUpdateStatus != null)
            return;

        RelativeLayout layout = (RelativeLayout) findViewById(R.id.layout);

        mUpdateStatus = new TextView(this);

        LayoutParams params = new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT);

        mUpdateStatus.setGravity(Gravity.CENTER);
        mUpdateStatus.setLayoutParams(params);

        layout.addView(mUpdateStatus);
    }

    private void createUpdateLayout() {

        lv.setVisibility(View.GONE);
        findViewById(R.id.textView).setVisibility(View.GONE);

        createUpdateStatusText();

        mUpdateStatus.setText(UPDATE_MESSAGE.replace("#STATUS#", "..."));

        mUpdateReceiver.register(MainActivity.this);

        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB)
            invalidateOptionsMenu();
    }

    private void createOfflineLayout() {

        lv.setVisibility(View.GONE);
        findViewById(R.id.textView).setVisibility(View.GONE);

        createUpdateStatusText();

        mUpdateStatus.setText(getString(R.string.no_connectivity));

        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB)
            invalidateOptionsMenu();
    }

    public void createOnlineLayout() {
        findViewById(R.id.textView).setVisibility(View.VISIBLE);
        lv.setVisibility(View.VISIBLE);

        if (mUpdateStatus != null)
            mUpdateStatus.setVisibility(View.GONE);

        if (mTargetAdapter != null) {
            mTargetAdapter.notifyDataSetChanged();
            return;
        }

        mTargetAdapter = new TargetAdapter();

        lv.setAdapter(mTargetAdapter);

        lv.setOnItemLongClickListener(new OnItemLongClickListener() {
            @Override
            public boolean onItemLongClick(AdapterView<?> parent, View view, int position, long id) {
                Target t = (Target) mTargetAdapter.getItem(position);
                if (t.getType() == Target.Type.NETWORK) {
                    if (mActionMode == null)
                        targetAliasPrompt(t);
                    return true;
                }
                if (mActionMode == null) {
                    mTargetAdapter.clearSelection();
                    mActionMode = startSupportActionMode(mActionModeCallback);
                }
                mTargetAdapter.toggleSelection(position);
                return true;
            }
        });

        mRadarReceiver.register(MainActivity.this);
        mUpdateReceiver.register(MainActivity.this);
        mWipeReceiver.register(MainActivity.this);
        mMsfReceiver.register(MainActivity.this);

        System.setTargetListObserver(mTargetAdapter);

        StartRPCServer();

        // if called for the second time after wifi connection
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB)
            invalidateOptionsMenu();
    }

    @Override
    protected void onActivityResult(int requestCode, int resultCode, Intent intent) {
        if (requestCode == WIFI_CONNECTION_REQUEST && resultCode == RESULT_OK
                && intent.hasExtra(WifiScannerActivity.CONNECTED)) {
            System.reloadNetworkMapping();
            if (mTargetAdapter != null) {
                mTargetAdapter.notifyDataSetChanged();
            }
            try {
                onCreate(null);
            } catch (IllegalStateException e) {
                // already attached.  don't reattach.
            }
        }
    }

    private void createLayout() {
        boolean wifiAvailable = Network.isWifiConnected(this);
        boolean connectivityAvailable = wifiAvailable || Network.isConnectivityAvailable(this);
        boolean coreBeating = System.isCoreInitialized();

        if (coreBeating && wifiAvailable) {
            createOnlineLayout();
        } else if (connectivityAvailable) {
            createUpdateLayout();
        } else {
            createOfflineLayout();
        }
    }

    private void onInitializationError(final String message) {
        MainActivity.this.runOnUiThread(new Runnable() {
            @Override
            public void run() {
                new FatalDialog(getString(R.string.initialization_error), message, message.contains(">"),
                        MainActivity.this).show();
            }
        });
    }

    private boolean startCore() {
        isRootMissing = false;
        try {
            System.initCore();

            return true;
        } catch (System.SuException e) {
            onInitializationError(getString(R.string.only_4_root));
            isRootMissing = true;
        } catch (System.DaemonException e) {
            Logger.error(e.getMessage());
        }

        return false;
    }

    private void onCoreBeating() {
        if (Client.hadCrashed()) {
            Logger.warning("Client has previously crashed, building a crash report.");
            CrashReporter.notifyNativeLibraryCrash();
            onInitializationError(getString(R.string.JNI_crash_detected));
        }
    }

    private void onCoreUpdated() {
        if (startCore()) {
            onCoreBeating();
        } else if (isRootMissing) {
            return;
        }

        MainActivity.this.runOnUiThread(new Runnable() {
            @Override
            public void run() {
                System.reloadNetworkMapping();
                createLayout();
                if (System.isInitialized())
                    startNetworkRadar();
            }
        });
    }

    @Override
    public void onCreate(Bundle savedInstanceState) {
        SharedPreferences themePrefs = getSharedPreferences("THEME", 0);
        Boolean isDark = themePrefs.getBoolean("isDark", false);
        boolean connectivityAvailable;

        if (isDark)
            setTheme(R.style.DarkTheme);
        else
            setTheme(R.style.AppTheme);
        super.onCreate(savedInstanceState);
        setContentView(R.layout.target_layout);

        lv = (ListView) findViewById(R.id.android_list);
        lv.setOnItemClickListener(new ListView.OnItemClickListener() {

            @Override
            public void onItemClick(AdapterView<?> parent, View view, int position, long id) {

                if (mActionMode != null) {
                    ((TargetAdapter) lv.getAdapter()).toggleSelection(position);
                    return;
                }

                Target target = (Target) mTargetAdapter.getItem(position);
                System.setCurrentTarget(target);

                new Thread(new Runnable() {
                    @Override
                    public void run() {

                        startActivityForResult(new Intent(MainActivity.this, ActionActivity.class),
                                WIFI_CONNECTION_REQUEST);

                        overridePendingTransition(R.anim.fadeout, R.anim.fadein);

                    }
                }).start();

                Toast.makeText(MainActivity.this, getString(R.string.selected_) + System.getCurrentTarget(),
                        Toast.LENGTH_SHORT).show();

            }
        });

        isWifiAvailable = Network.isWifiConnected(this);
        connectivityAvailable = isWifiAvailable || Network.isConnectivityAvailable(this);

        // make sure system object was correctly initialized during application
        // startup
        if (!System.isInitialized()) {
            // wifi available but system failed to initialize, this is a fatal
            // :(
            if (isWifiAvailable) {

                // retry
                try {
                    System.init(MainActivity.this.getApplicationContext());

                    System.registerPlugin(new RouterPwn());
                    System.registerPlugin(new Traceroute());
                    System.registerPlugin(new PortScanner());
                    System.registerPlugin(new Inspector());
                    System.registerPlugin(new ExploitFinder());
                    System.registerPlugin(new LoginCracker());
                    System.registerPlugin(new Sessions());
                    System.registerPlugin(new MITM());
                    System.registerPlugin(new PacketForger());
                } catch (Exception e) {
                    if (!(e instanceof NoRouteToHostException))
                        System.errorLogging(e);

                    onInitializationError(System.getLastError());

                    return;
                }
            }
        } else {
            System.reloadNetworkMapping();
        }

        boolean coreInstalled = System.isCoreInstalled();
        boolean coreBeating = System.isCoreInitialized();

        if (coreInstalled && !coreBeating) {
            coreBeating = startCore();
            if (coreBeating) {
                onCoreBeating();
            } else if (isRootMissing) {
                return;
            }
        }

        if (!connectivityAvailable) {
            if (!coreInstalled) {
                onInitializationError(getString(R.string.no_core_no_connectivity));
                return;
            } else if (!coreBeating) {
                onInitializationError(getString(R.string.heart_attack));
                return;
            }
        }

        if (!coreInstalled) {
            UPDATE_MESSAGE = getString(R.string.missing_core_update);
        } else if (!coreBeating) {
            UPDATE_MESSAGE = getString(R.string.heart_attack_update);
        } else if (!isWifiAvailable) {
            UPDATE_MESSAGE = getString(R.string.no_wifi_available);
        }

        if (connectivityAvailable)
            startUpdateChecker();

        if (coreBeating && isWifiAvailable)
            startNetworkRadar();

        createLayout();
    }

    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        MenuInflater inflater = getMenuInflater();
        inflater.inflate(R.menu.main, menu);

        if (!isWifiAvailable) {
            menu.findItem(R.id.add).setVisible(false);
            menu.findItem(R.id.scan).setVisible(false);
            menu.findItem(R.id.new_session).setEnabled(false);
            menu.findItem(R.id.save_session).setEnabled(false);
            menu.findItem(R.id.restore_session).setEnabled(false);
            menu.findItem(R.id.ss_monitor).setEnabled(false);
            menu.findItem(R.id.ss_msfrpcd).setEnabled(false);
        }

        mMenu = menu;

        return super.onCreateOptionsMenu(menu);
    }

    @Override
    public boolean onPrepareOptionsMenu(Menu menu) {
        MenuItem item = menu.findItem(R.id.ss_monitor);

        Services.getNetworkRadar().buildMenuItem(item);

        item = menu.findItem(R.id.ss_msfrpcd);

        Services.getMsfRpcdService().buildMenuItem(item);

        mMenu = menu;

        return super.onPrepareOptionsMenu(menu);
    }

    private void targetAliasPrompt(final Target target) {

        new InputDialog(getString(R.string.target_alias), getString(R.string.set_alias),
                target.hasAlias() ? target.getAlias() : "", true, false, MainActivity.this,
                new InputDialogListener() {
                    @Override
                    public void onInputEntered(String input) {
                        target.setAlias(input);
                        mTargetAdapter.notifyDataSetChanged();
                    }
                }).show();
    }

    private ActionMode.Callback mActionModeCallback = new ActionMode.Callback() {

        public boolean onCreateActionMode(ActionMode mode, Menu menu) {
            MenuInflater inflater = mode.getMenuInflater();
            inflater.inflate(R.menu.main_multi, menu);
            return true;
        }

        public boolean onPrepareActionMode(ActionMode mode, Menu menu) {
            int i = mTargetAdapter.getSelectedCount();
            mode.setTitle(i + " " + getString((i > 1 ? R.string.targets_selected : R.string.target_selected)));
            MenuItem item = menu.findItem(R.id.multi_action);
            if (item != null)
                item.setIcon((i > 1 ? android.R.drawable.ic_dialog_dialer : android.R.drawable.ic_menu_edit));
            return false;
        }

        public boolean onActionItemClicked(ActionMode mode, MenuItem item) {
            ArrayList<Plugin> commonPlugins = null;

            switch (item.getItemId()) {
            case R.id.multi_action:
                final int[] selected = mTargetAdapter.getSelectedPositions();
                if (selected.length > 1) {
                    Target target = (Target) mTargetAdapter.getItem(selected[0]);
                    commonPlugins = System.getPluginsForTarget(target);
                    for (int i = 1; i < selected.length; i++) {
                        target = (Target) mTargetAdapter.getItem(selected[i]);
                        ArrayList<Plugin> targetPlugins = System.getPluginsForTarget(target);
                        ArrayList<Plugin> removeThem = new ArrayList<Plugin>();
                        for (Plugin p : commonPlugins) {
                            if (!targetPlugins.contains(p))
                                removeThem.add(p);
                        }
                        for (Plugin p : removeThem) {
                            commonPlugins.remove(p);
                        }
                    }
                    if (commonPlugins.size() > 0) {
                        final int[] actions = new int[commonPlugins.size()];
                        for (int i = 0; i < actions.length; i++)
                            actions[i] = commonPlugins.get(i).getName();

                        (new MultipleChoiceDialog(R.string.choose_method, actions, MainActivity.this,
                                new MultipleChoiceDialog.MultipleChoiceDialogListener() {
                                    @Override
                                    public void onChoice(int[] choices) {
                                        Intent intent = new Intent(MainActivity.this, MultiAttackService.class);
                                        int[] selectedActions = new int[choices.length];

                                        for (int i = 0; i < selectedActions.length; i++)
                                            selectedActions[i] = actions[choices[i]];

                                        intent.putExtra(MultiAttackService.MULTI_TARGETS, selected);
                                        intent.putExtra(MultiAttackService.MULTI_ACTIONS, selectedActions);

                                        startService(intent);
                                    }
                                })).show();
                    } else {
                        (new ErrorDialog(getString(R.string.error), "no common actions found", MainActivity.this))
                                .show();
                    }
                } else {
                    targetAliasPrompt((Target) mTargetAdapter.getItem(selected[0]));
                }
                mode.finish(); // Action picked, so close the CAB
                return true;
            default:
                return false;
            }
        }

        // called when the user exits the action mode
        public void onDestroyActionMode(ActionMode mode) {
            mActionMode = null;
            mTargetAdapter.clearSelection();
        }
    };

    public void startUpdateChecker() {
        if (System.getSettings().getBoolean("PREF_CHECK_UPDATES", true)) {
            new UpdateChecker(this).start();
        } else {
            MainActivity.this.sendBroadcast(new Intent(UPDATE_NOT_AVAILABLE));
        }
    }

    public void startNetworkRadar() {
        new Thread(new Runnable() {
            @Override
            public void run() {
                Services.getNetworkRadar().start();
            }
        }).start();
    }

    public void stopNetworkRadar() {
        new Thread(new Runnable() {
            @Override
            public void run() {
                Services.getNetworkRadar().stop();
            }
        }).start();
    }

    /**
     * start MSF RPC Daemon
     */
    public void StartRPCServer() {
        new Thread(new Runnable() {
            @Override
            public void run() {
                if (Services.getMsfRpcdService().isAvailable())
                    Services.getMsfRpcdService().start();
            }
        }).start();
    }

    /**
     * stop MSF RPC Daemon
     */
    public void StopRPCServer() {
        new Thread(new Runnable() {
            @Override
            public void run() {
                Services.getMsfRpcdService().stop();
            }
        }).start();
    }

    @Override
    public boolean onOptionsItemSelected(final MenuItem item) {
        switch (item.getItemId()) {

        case R.id.add:
            new InputDialog(getString(R.string.add_custom_target), getString(R.string.enter_url), MainActivity.this,
                    new InputDialogListener() {
                        @Override
                        public void onInputEntered(String input) {
                            final Target target = Target.getFromString(input);
                            if (target != null) {
                                ThreadHelper.getSharedExecutor().execute(new Runnable() {
                                    @Override
                                    public void run() {
                                        System.addOrderedTarget(target);
                                        mTargetAdapter.update(null, null);
                                    }
                                });
                            } else
                                new ErrorDialog(getString(R.string.error), getString(R.string.invalid_target),
                                        MainActivity.this).show();
                        }
                    }).show();
            return true;

        case R.id.scan:
            startNetworkRadar();
            return true;

        case R.id.wifi_scan:
            stopNetworkRadar();

            mRadarReceiver.unregister();
            mUpdateReceiver.unregister();

            startActivityForResult(new Intent(MainActivity.this, WifiScannerActivity.class),
                    WIFI_CONNECTION_REQUEST);
            overridePendingTransition(R.anim.fadeout, R.anim.fadein);
            return true;

        case R.id.new_session:
            new ConfirmDialog(getString(R.string.warning), getString(R.string.warning_new_session), this,
                    new ConfirmDialogListener() {
                        @Override
                        public void onConfirm() {
                            try {
                                System.reset();
                                mTargetAdapter.notifyDataSetChanged();

                                Toast.makeText(MainActivity.this, getString(R.string.new_session_started),
                                        Toast.LENGTH_SHORT).show();
                            } catch (Exception e) {
                                new FatalDialog(getString(R.string.error), e.toString(), MainActivity.this).show();
                            }
                        }

                        @Override
                        public void onCancel() {
                        }

                    }).show();

            return true;

        case R.id.save_session:
            new InputDialog(getString(R.string.save_session), getString(R.string.enter_session_name),
                    System.getSessionName(), true, false, MainActivity.this, new InputDialogListener() {
                        @Override
                        public void onInputEntered(String input) {
                            String name = input.trim().replace("/", "").replace("..", "");

                            if (!name.isEmpty()) {
                                try {
                                    String filename = System.saveSession(name);

                                    Toast.makeText(MainActivity.this,
                                            getString(R.string.session_saved_to) + filename + " .",
                                            Toast.LENGTH_SHORT).show();
                                } catch (IOException e) {
                                    new ErrorDialog(getString(R.string.error), e.toString(), MainActivity.this)
                                            .show();
                                }
                            } else
                                new ErrorDialog(getString(R.string.error), getString(R.string.invalid_session),
                                        MainActivity.this).show();
                        }
                    }).show();
            return true;

        case R.id.restore_session:
            final ArrayList<String> sessions = System.getAvailableSessionFiles();

            if (sessions != null && sessions.size() > 0) {
                new SpinnerDialog(getString(R.string.select_session), getString(R.string.select_session_file),
                        sessions.toArray(new String[sessions.size()]), MainActivity.this,
                        new SpinnerDialogListener() {
                            @Override
                            public void onItemSelected(int index) {
                                String session = sessions.get(index);

                                try {
                                    System.loadSession(session);
                                    mTargetAdapter.notifyDataSetChanged();
                                } catch (Exception e) {
                                    e.printStackTrace();
                                    new ErrorDialog(getString(R.string.error), e.getMessage(), MainActivity.this)
                                            .show();
                                }
                            }
                        }).show();
            } else
                new ErrorDialog(getString(R.string.error), getString(R.string.no_session_found), MainActivity.this)
                        .show();
            return true;

        case R.id.settings:
            startActivity(new Intent(MainActivity.this, SettingsActivity.class));
            overridePendingTransition(R.anim.fadeout, R.anim.fadein);
            return true;

        case R.id.ss_monitor:
            new Thread(new Runnable() {
                @Override
                public void run() {
                    Services.getNetworkRadar().onMenuClick(MainActivity.this, item);
                }
            }).start();
            return true;

        case R.id.ss_msfrpcd:
            new Thread(new Runnable() {
                @Override
                public void run() {
                    Services.getMsfRpcdService().onMenuClick(MainActivity.this, item);
                }
            }).start();
            return true;

        case R.id.submit_issue:
            String uri = getString(R.string.github_new_issue_url);
            Intent browser = new Intent(Intent.ACTION_VIEW, Uri.parse(uri));
            startActivity(browser);
            // for fat-tire:
            //   String.format(getString(R.string.issue_message), getString(R.string.github_issues_url), getString(R.string.github_new_issue_url));
            return true;

        case R.id.about:
            new AboutDialog(this).show();
            return true;

        default:
            return super.onOptionsItemSelected(item);
        }
    }

    @Override
    public void onBackPressed() {
        if (mLastBackPressTime < java.lang.System.currentTimeMillis() - 4000) {
            mToast = Toast.makeText(this, getString(R.string.press_back), Toast.LENGTH_SHORT);
            mToast.show();
            mLastBackPressTime = java.lang.System.currentTimeMillis();
        } else {
            if (mToast != null)
                mToast.cancel();

            new ConfirmDialog(getString(R.string.exit), getString(R.string.close_confirm), this,
                    new ConfirmDialogListener() {
                        @Override
                        public void onConfirm() {
                            MainActivity.this.finish();
                        }

                        @Override
                        public void onCancel() {
                        }
                    }).show();

            mLastBackPressTime = 0;
        }
    }

    @Override
    public void onDestroy() {
        stopNetworkRadar();
        StopRPCServer();

        mRadarReceiver.unregister();
        mUpdateReceiver.unregister();
        mWipeReceiver.unregister();
        mMsfReceiver.unregister();

        // make sure no zombie process is running before destroying the activity
        System.clean(true);

        super.onDestroy();
    }

    public class TargetAdapter extends BaseAdapter implements Runnable, Observer {

        private List<Target> list = System.getTargets();
        private boolean isDark = getSharedPreferences("THEME", 0).getBoolean("isDark", false);

        @Override
        public int getCount() {
            return list.size();
        }

        @Override
        public Object getItem(int position) {
            return list.get(position);
        }

        @Override
        public long getItemId(int position) {
            return R.layout.target_list_item;
        }

        @Override
        public View getView(int position, View convertView, ViewGroup parent) {
            View row = convertView;
            TargetHolder holder;

            if (row == null) {
                LayoutInflater inflater = (LayoutInflater) MainActivity.this
                        .getSystemService(Context.LAYOUT_INFLATER_SERVICE);
                row = inflater.inflate(R.layout.target_list_item, parent, false);

                if (isDark)
                    row.setBackgroundResource(R.drawable.card_background_dark);

                holder = new TargetHolder();
                holder.itemImage = (ImageView) (row != null ? row.findViewById(R.id.itemIcon) : null);
                holder.itemTitle = (TextView) (row != null ? row.findViewById(R.id.itemTitle) : null);
                holder.itemDescription = (TextView) (row != null ? row.findViewById(R.id.itemDescription) : null);
                holder.portCount = (TextView) (row != null ? row.findViewById(R.id.portCount) : null);
                holder.portCountLayout = (LinearLayout) (row != null ? row.findViewById(R.id.portCountLayout)
                        : null);
                if (isDark)
                    holder.portCountLayout.setBackgroundResource(R.drawable.rounded_square_grey);
                if (row != null)
                    row.setTag(holder);
            } else
                holder = (TargetHolder) row.getTag();

            final Target target = list.get(position);

            if (target.hasAlias()) {
                holder.itemTitle.setText(Html.fromHtml(
                        "<b>" + target.getAlias() + "</b> <small>( " + target.getDisplayAddress() + " )</small>"));
            } else {
                holder.itemTitle.setText(target.toString());
            }
            holder.itemTitle.setTextColor(ContextCompat.getColor(getApplicationContext(),
                    (target.isConnected() ? R.color.app_color : R.color.gray_text)));

            holder.itemTitle.setTypeface(null, Typeface.NORMAL);
            holder.itemImage.setImageResource(target.getDrawableResourceId());
            holder.itemDescription.setText(target.getDescription());

            int openedPorts = target.getOpenPorts().size();

            holder.portCount.setText(String.format("%d", openedPorts));
            holder.portCountLayout.setVisibility(openedPorts < 1 ? View.GONE : View.VISIBLE);
            return row;
        }

        public void clearSelection() {
            for (Target t : list)
                t.setSelected(false);
            notifyDataSetChanged();
            if (mActionMode != null)
                mActionMode.finish();
        }

        public void toggleSelection(int position) {
            Target t = list.get(position);
            t.setSelected(!t.isSelected());
            notifyDataSetChanged();
            if (mActionMode != null) {
                if (getSelectedCount() > 0)
                    mActionMode.invalidate();
                else
                    mActionMode.finish();
            }
        }

        public int getSelectedCount() {
            int i = 0;
            for (Target t : list)
                if (t.isSelected())
                    i++;
            return i;
        }

        public ArrayList<Target> getSelected() {
            ArrayList<Target> result = new ArrayList<Target>();
            for (Target t : list)
                if (t.isSelected())
                    result.add(t);
            return result;
        }

        public int[] getSelectedPositions() {
            int[] res = new int[getSelectedCount()];
            int j = 0;

            for (int i = 0; i < list.size(); i++)
                if (list.get(i).isSelected())
                    res[j++] = i;
            return res;
        }

        @Override
        public void update(Observable observable, Object data) {
            final Target target = (Target) data;

            if (target == null) {
                // update the whole list
                MainActivity.this.runOnUiThread(this);
                return;
            }

            // update only a row, if it's displayed
            MainActivity.this.runOnUiThread(new Runnable() {
                @Override
                public void run() {
                    if (lv == null)
                        return;
                    int start = lv.getFirstVisiblePosition();
                    for (int i = start, j = lv.getLastVisiblePosition(); i <= j; i++)
                        if (target == list.get(i)) {
                            View view = lv.getChildAt(i - start);
                            getView(i, view, lv);
                            break;
                        }
                }
            });

        }

        @Override
        public void run() {
            list = System.getTargets();
            notifyDataSetChanged();
        }

        class TargetHolder {
            ImageView itemImage;
            TextView itemTitle;
            TextView itemDescription;
            TextView portCount;
            LinearLayout portCountLayout;
        }
    }

    private class WipeReceiver extends ManagedReceiver {
        private IntentFilter mFilter = null;

        public WipeReceiver() {
            mFilter = new IntentFilter();

            mFilter.addAction(SettingsActivity.SETTINGS_WIPE_START);
        }

        public IntentFilter getFilter() {
            return mFilter;
        }

        @Override
        public void onReceive(Context context, Intent intent) {

            if (intent.getAction().equals(SettingsActivity.SETTINGS_WIPE_START)) {
                try {
                    String path;

                    if (intent.hasExtra(SettingsActivity.SETTINGS_WIPE_DIR)) {
                        path = intent.getStringExtra(SettingsActivity.SETTINGS_WIPE_DIR);
                    } else {
                        path = System.getRubyPath() + "' '" + System.getMsfPath();
                    }

                    StopRPCServer();
                    System.getTools().raw.async("rm -rf '" + path + "'", new Child.EventReceiver() {
                        @Override
                        public void onEnd(int exitCode) {
                            MainActivity.this.sendBroadcast(new Intent(SettingsActivity.SETTINGS_WIPE_DONE));
                        }

                        @Override
                        public void onDeath(int signal) {
                            MainActivity.this.sendBroadcast(new Intent(SettingsActivity.SETTINGS_WIPE_DONE));
                        }

                        @Override
                        public void onEvent(Event e) {
                        }
                    });
                } catch (Exception e) {
                    System.errorLogging(e);
                }
            }
        }
    }

    private class UpdateReceiver extends ManagedReceiver {
        private IntentFilter mFilter = null;

        public UpdateReceiver() {
            mFilter = new IntentFilter();

            mFilter.addAction(UPDATE_CHECKING);
            mFilter.addAction(UPDATE_AVAILABLE);
            mFilter.addAction(UPDATE_NOT_AVAILABLE);
            mFilter.addAction(UpdateService.ERROR);
            mFilter.addAction(UpdateService.DONE);
        }

        public IntentFilter getFilter() {
            return mFilter;
        }

        private void onUpdateAvailable(final Update update, final boolean mandatory) {
            MainActivity.this.runOnUiThread(new Runnable() {
                @Override
                public void run() {
                    new ConfirmDialog(getString(R.string.update_available), update.prompt, MainActivity.this,
                            new ConfirmDialogListener() {
                                @Override
                                public void onConfirm() {
                                    StopRPCServer();
                                    Intent i = new Intent(MainActivity.this, UpdateService.class);
                                    i.setAction(UpdateService.START);
                                    i.putExtra(UpdateService.UPDATE, update);

                                    startService(i);
                                }

                                @Override
                                public void onCancel() {
                                    if (!mandatory) {
                                        return;
                                    }

                                    onInitializationError(getString(R.string.mandatory_update));
                                }
                            }).show();
                }
            });
        }

        private void onUpdateAvailable(Update update) {
            onUpdateAvailable(update, (update instanceof CoreUpdate) && !System.isCoreInstalled());
        }

        private void onUpdateDone(Update update) {

            System.reloadTools();

            if ((update instanceof MsfUpdate) || (update instanceof RubyUpdate)) {
                StartRPCServer();
            }

            if (update instanceof CoreUpdate) {
                onCoreUpdated();
            }

            // restart update checker after a successful update
            startUpdateChecker();
        }

        private void onUpdateError(final Update update, final int message) {

            if (update instanceof CoreUpdate) {
                onInitializationError(getString(message));
                return;
            }

            MainActivity.this.runOnUiThread(new Runnable() {
                @Override
                public void run() {
                    new ErrorDialog(getString(R.string.error), getString(message), MainActivity.this).show();
                }
            });

            System.reloadTools();
        }

        @SuppressWarnings("ConstantConditions")
        @Override
        public void onReceive(Context context, Intent intent) {
            String action = intent.getAction();
            Update update = null;

            if (intent.hasExtra(UpdateService.UPDATE)) {
                update = (Update) intent.getSerializableExtra(UpdateService.UPDATE);
            }

            switch (action) {
            case UPDATE_CHECKING:
                if (mUpdateStatus != null)
                    mUpdateStatus.setText(UPDATE_MESSAGE.replace("#STATUS#", getString(R.string.checking)));
                break;
            case UPDATE_NOT_AVAILABLE:
                if (mUpdateStatus != null)
                    mUpdateStatus
                            .setText(UPDATE_MESSAGE.replace("#STATUS#", getString(R.string.no_updates_available)));

                if (!System.isCoreInstalled()) {
                    onInitializationError(getString(R.string.no_core_found));
                }
                break;
            case UPDATE_AVAILABLE:
                onUpdateAvailable(update);
                break;
            case UpdateService.DONE:
                onUpdateDone(update);
                break;
            case UpdateService.ERROR:
                int message = intent.getIntExtra(UpdateService.MESSAGE, R.string.error_occured);
                onUpdateError(update, message);
                break;
            }
        }
    }
}