com.hijacker.MainActivity.java Source code

Java tutorial

Introduction

Here is the source code for com.hijacker.MainActivity.java

Source

package com.hijacker;

/*
Copyright (C) 2016  Christos Kyriakopoylos
    
This program 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.
    
This program 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 this program.  If not, see <http://www.gnu.org/licenses/>
 */

import android.Manifest;
import android.app.Activity;
import android.app.FragmentManager;
import android.app.FragmentTransaction;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.content.ClipData;
import android.content.ClipboardManager;
import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.graphics.drawable.Drawable;
import android.net.ConnectivityManager;
import android.net.NetworkInfo;
import android.net.Uri;
import android.os.AsyncTask;
import android.os.Build;
import android.os.Bundle;
import android.os.Environment;
import android.os.Handler;
import android.os.Looper;
import android.preference.PreferenceManager;
import android.support.annotation.NonNull;
import android.support.design.widget.Snackbar;
import android.support.v4.app.ActivityCompat;
import android.support.v4.content.ContextCompat;
import android.support.v4.widget.DrawerLayout;
import android.support.v7.app.ActionBar;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.app.NotificationCompat;
import android.support.v7.widget.Toolbar;
import android.util.Log;
import android.view.KeyEvent;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.view.ViewGroup;
import android.widget.AdapterView;
import android.widget.ArrayAdapter;
import android.widget.Button;
import android.widget.ImageView;
import android.widget.ListView;
import android.widget.ProgressBar;
import android.widget.TextView;
import android.widget.Toast;

import com.google.android.gms.appindexing.Action;
import com.google.android.gms.appindexing.AppIndex;
import com.google.android.gms.appindexing.Thing;
import com.google.android.gms.common.api.GoogleApiClient;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileOutputStream;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.Inet4Address;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.Socket;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;

import static com.hijacker.AP.getAPByMac;
import static com.hijacker.CustomAction.TYPE_ST;
import static com.hijacker.Device.trimMac;
import static com.hijacker.IsolatedFragment.is_ap;
import static com.hijacker.MDKFragment.ados;
import static com.hijacker.MDKFragment.ados_pid;
import static com.hijacker.MDKFragment.bf;
import static com.hijacker.MDKFragment.bf_pid;
import static com.hijacker.Shell.getFreeShell;
import static com.hijacker.Shell.runOne;

public class MainActivity extends AppCompatActivity {
    static String AUTH_KEY = "key-that-will-be-changed-in-the-release-build_this-is-for-testing";
    static final String SERVER = "hijacker-android.ddns.net";
    static int PORT = 1025;
    static final String NETHUNTER_BOOTKALI_BASH = "/data/data/com.offsec.nethunter/files/scripts/bootkali_bash";
    static final String REQ_VERSION = "version", REQ_INFO = "info", REQ_EXIT = "exit", REQ_REPORT = "report",
            REQ_FEEDBACK = "feedback", REQ_NEW_ID = "newid";
    static final String ANS_POSITIVE = "OK", ANS_NEGATIVE = "NO";
    static final int BUFFER_SIZE = 1048576;
    static final int AIREPLAY_DEAUTH = 1, AIREPLAY_WEP = 2;
    static final int FRAGMENT_AIRODUMP = 0, FRAGMENT_MDK = 1, FRAGMENT_CRACK = 2, FRAGMENT_REAVER = 3,
            FRAGMENT_CUSTOM = 4, FRAGMENT_SETTINGS = 5; //These need to correspond to the items in the drawer
    static final int PROCESS_AIRODUMP = 0, PROCESS_AIREPLAY = 1, PROCESS_MDK = 2, PROCESS_AIRCRACK = 3,
            PROCESS_REAVER = 4;
    static final int MDK_BF = 0, MDK_ADOS = 1;
    static final int SORT_NOSORT = 0, SORT_ESSID = 1, SORT_BEACONS_FRAMES = 2, SORT_DATA_FRAMES = 3, SORT_PWR = 4;
    static final int CHROOT_FOUND = 0, CHROOT_BIN_MISSING = 1, CHROOT_DIR_MISSING = 2, CHROOT_BOTH_MISSING = 3;
    //State variables
    static boolean wpacheckcont = false;
    static boolean notif_on = false, background = false; //notif_on: notification should be shown, background: the app is running in the background
    static int aireplay_running = 0, currentFragment = FRAGMENT_AIRODUMP; //Set currentFragment in onResume of each Fragment
    //Filters
    static boolean show_ap = true, show_st = true, show_na_st = true, wpa = true, wep = true, opn = true;
    static boolean show_ch[] = { true, false, false, false, false, false, false, false, false, false, false, false,
            false, false, false };
    static int pwr_filter = 120;
    static String manuf_filter = "";
    //Airodump list sort 
    static int sort = SORT_NOSORT;
    static boolean sort_reverse = false;
    static boolean toSort = false; //Variable to mark that the list must be sorted, so Tile.sort() must be called
    //Views that need to be accessible globally
    static TextView ap_count, st_count; //AP and ST count textviews in toolbar
    static ProgressBar progress;
    static Toolbar toolbar;
    static View rootView;
    static DrawerLayout mDrawerLayout;
    static ListView mDrawerList;
    static String[] mPlanetTitles;
    static Drawable overflow[] = { null, null, null, null, null, null, null, null }; //Drawables to use for overflow button icon
    static ImageView status[] = { null, null, null, null, null }; //Icons in TestDialog, set in TestDialog class
    static int progress_int;
    static long last_action; //Timestamp for the last action. Used in watchdog to avoid false positives
    static Thread wpa_thread;
    static Runnable wpa_runnable;
    static Menu menu;
    static MyListAdapter adapter;
    static CustomActionAdapter custom_action_adapter;
    static FileExplorerAdapter file_explorer_adapter;
    static SharedPreferences pref;
    static SharedPreferences.Editor pref_edit;
    static ClipboardManager clipboard;
    static NotificationCompat.Builder notif, error_notif, handshake_notif;
    static NotificationManager mNotificationManager;
    static FragmentManager mFragmentManager;
    static String path, data_path, actions_path, firm_backup_file, manufDBFile, arch, busybox; //path: App files path (ends with .../files)
    static File aliases_file;
    static FileWriter aliases_in;
    static final HashMap<String, String> aliases = new HashMap<>();
    static HashMap<String, String> manufHashMap;
    //App and device info
    static String versionName, deviceModel;
    static int versionCode;
    static long deviceID;
    static boolean init = false; //True on first run to swap the dialogs for initialization
    static ActionBar actionBar;
    static String bootkali_init_bin = "bootkali_init";
    //Preferences - Defaults are in strings.xml
    static String iface, prefix, airodump_dir, aireplay_dir, aircrack_dir, mdk3_dir, reaver_dir, cap_dir,
            chroot_dir, enable_monMode, disable_monMode, custom_chroot_cmd;
    static int deauthWait;
    static boolean show_notif, show_details, airOnStartup, debug, delete_extra, monstart, always_cap, cont_on_fail,
            watchdog, target_deauth, enable_on_airodump, update_on_startup;

    private GoogleApiClient client;
    Runnable onResumeRunnable = null;
    WatchdogTask watchdogTask;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        Thread.setDefaultUncaughtExceptionHandler(new Thread.UncaughtExceptionHandler() {
            @Override
            public void uncaughtException(Thread thread, Throwable throwable) {
                throwable.printStackTrace();
                String stackTrace = "";
                stackTrace += throwable.getMessage() + '\n';
                for (int i = 0; i < throwable.getStackTrace().length; i++) {
                    stackTrace += throwable.getStackTrace()[i].toString() + '\n';
                }

                Intent intent = new Intent();
                intent.setAction("com.hijacker.SendLogActivity");
                intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
                intent.putExtra("exception", stackTrace);
                startActivity(intent);

                finish();
                System.exit(1);
            }
        });
        adapter = new MyListAdapter(); //ALWAYS BEFORE setContentView AND setup(), can't stress it enough...
        adapter.setNotifyOnChange(true);
        custom_action_adapter = new CustomActionAdapter();
        custom_action_adapter.setNotifyOnChange(true);
        file_explorer_adapter = new FileExplorerAdapter();
        file_explorer_adapter.setNotifyOnChange(true);
        setContentView(R.layout.activity_main);
        setSupportActionBar((Toolbar) findViewById(R.id.my_toolbar));

        //Google AppIndex
        client = new GoogleApiClient.Builder(MainActivity.this).addApi(AppIndex.API).build();

        //fullSetup();
        new SetupTask().execute();
    }

    private class SetupTask extends AsyncTask<Void, String, Boolean> {
        LoadingDialog loadingDialog;

        @Override
        protected void onPreExecute() {
            loadingDialog = new LoadingDialog();
            loadingDialog.setInitText(getString(R.string.starting_hijacker));
            loadingDialog.show(getFragmentManager(), "LoadingDialog");

            //Initialize the drawer
            mPlanetTitles = getResources().getStringArray(R.array.planets_array);
            mDrawerLayout = (DrawerLayout) findViewById(R.id.drawer_layout);
            mDrawerList = (ListView) findViewById(R.id.left_drawer);
            mDrawerList.setAdapter(new ArrayAdapter<>(MainActivity.this, R.layout.drawer_list_item,
                    R.id.navDrawerTv, mPlanetTitles));
            mDrawerList.setOnItemClickListener(new DrawerItemClickListener());
            toolbar = (Toolbar) findViewById(R.id.my_toolbar);
            toolbar.setOverflowIcon(overflow[0]);
        }

        @Override
        protected Boolean doInBackground(Void... params) {
            Looper.prepare();
            //Initialize managers
            publishProgress(getString(R.string.init_managers));
            pref = PreferenceManager.getDefaultSharedPreferences(MainActivity.this);
            pref_edit = pref.edit();
            clipboard = (ClipboardManager) getSystemService(CLIPBOARD_SERVICE);
            mNotificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
            mFragmentManager = getFragmentManager();

            //Load device information
            publishProgress(getString(R.string.loading_device_information));
            PackageManager manager = MainActivity.this.getPackageManager();
            PackageInfo info = null;
            try {
                info = manager.getPackageInfo(MainActivity.this.getPackageName(), 0);
                versionName = info.versionName.replace(" ", "_");
                versionCode = info.versionCode;
            } catch (PackageManager.NameNotFoundException e) {
                Log.e("HIJACKER/SetupTask", e.toString());
            }
            deviceModel = Build.MODEL;
            if (!deviceModel.startsWith(Build.MANUFACTURER))
                deviceModel = Build.MANUFACTURER + " " + deviceModel;
            deviceModel = deviceModel.replace(" ", "_");
            deviceID = pref.getLong("deviceID", -1);
            arch = System.getProperty("os.arch");

            //Find views
            publishProgress(getString(R.string.init_views));
            ap_count = (TextView) findViewById(R.id.ap_count);
            st_count = (TextView) findViewById(R.id.st_count);
            progress = (ProgressBar) findViewById(R.id.progressBar);
            rootView = findViewById(R.id.fragment1);
            overflow[0] = getDrawable(R.drawable.overflow0);
            overflow[1] = getDrawable(R.drawable.overflow1);
            overflow[2] = getDrawable(R.drawable.overflow2);
            overflow[3] = getDrawable(R.drawable.overflow3);
            overflow[4] = getDrawable(R.drawable.overflow4);
            overflow[5] = getDrawable(R.drawable.overflow5);
            overflow[6] = getDrawable(R.drawable.overflow6);
            overflow[7] = getDrawable(R.drawable.overflow7);
            actionBar = getSupportActionBar();

            //Load defaults
            publishProgress(getString(R.string.loading_defaults));
            iface = getString(R.string.iface);
            prefix = getString(R.string.prefix);
            cap_dir = getString(R.string.cap_dir);
            enable_monMode = getString(R.string.enable_monMode);
            disable_monMode = getString(R.string.disable_monMode);
            enable_on_airodump = Boolean.parseBoolean(getString(R.string.enable_on_airodump));
            deauthWait = Integer.parseInt(getString(R.string.deauthWait));
            show_notif = Boolean.parseBoolean(getString(R.string.show_notif));
            show_details = Boolean.parseBoolean(getString(R.string.show_details));
            airOnStartup = Boolean.parseBoolean(getString(R.string.airOnStartup));
            debug = Boolean.parseBoolean(getString(R.string.debug));
            delete_extra = Boolean.parseBoolean(getString(R.string.delete_extra));
            always_cap = Boolean.parseBoolean(getString(R.string.always_cap));
            chroot_dir = getString(R.string.chroot_dir);
            monstart = Boolean.parseBoolean(getString(R.string.monstart));
            custom_chroot_cmd = "";
            cont_on_fail = Boolean.parseBoolean(getString(R.string.cont_on_fail));
            watchdog = Boolean.parseBoolean(getString(R.string.watchdog));
            target_deauth = Boolean.parseBoolean(getString(R.string.target_deauth));
            update_on_startup = Boolean.parseBoolean(getString(R.string.auto_update));

            //Load preferences
            publishProgress(getString(R.string.loading_preferences));
            load();

            //Initialize paths
            publishProgress(getString(R.string.init_files));
            path = getFilesDir().getAbsolutePath();
            data_path = Environment.getExternalStorageDirectory() + "/Hijacker";
            actions_path = data_path + "/actions";
            firm_backup_file = data_path + "/fw_bcmdhd.orig.bin";
            manufDBFile = path + "/manuf.db";
            File data_dir = new File(data_path);
            if (!data_dir.exists()) {
                //Create directory, subdirectories and files
                data_dir.mkdir();

                //Move app files from other directories in /Hijacker
                File firm_backup_old = new File(Environment.getExternalStorageDirectory() + "/fw_bcmdhd.orig.bin");
                if (firm_backup_old.exists()) {
                    firm_backup_old.renameTo(new File(data_path + "/fw_bcmdhd.orig.bin"));
                }

                File actions_dir_old = new File(Environment.getExternalStorageDirectory() + "/Hijacker-actions");
                if (actions_dir_old.exists()) {
                    actions_dir_old.renameTo(new File(actions_path));
                } else {
                    new File(data_dir + "/actions").mkdir();
                }
            }

            //Initialize notifications
            publishProgress(getString(R.string.init_notifications));
            //Create intents
            Intent cancel_intent = new Intent(MainActivity.this, DismissReceiver.class);
            Intent stop_intent = new Intent(MainActivity.this, StopReceiver.class);
            Intent notificationIntent = new Intent(MainActivity.this, MainActivity.class);
            notificationIntent.setFlags(Intent.FLAG_ACTIVITY_REORDER_TO_FRONT);
            PendingIntent click_intent = PendingIntent.getActivity(MainActivity.this, 0, notificationIntent, 0);

            //Create 'running' notification
            notif = new NotificationCompat.Builder(MainActivity.this);
            notif.setContentTitle(getString(R.string.notification_title));
            notif.setContentText(" ");
            notif.setSmallIcon(R.drawable.ic_notification);
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
                notif.setColor(getColor(R.color.colorAccent));
            }
            notif.setDeleteIntent(
                    PendingIntent.getBroadcast(MainActivity.this.getApplicationContext(), 0, cancel_intent, 0));
            notif.addAction(R.drawable.stop_drawable, getString(R.string.stop_attacks),
                    PendingIntent.getBroadcast(MainActivity.this.getApplicationContext(), 0, stop_intent, 0));
            notif.setContentIntent(click_intent);

            //Create 'error' notification (used by watchdog)
            error_notif = new NotificationCompat.Builder(MainActivity.this);
            error_notif.setSmallIcon(R.drawable.ic_notification);
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
                error_notif.setColor(getColor(android.R.color.holo_red_dark));
            }
            error_notif.setContentIntent(click_intent);
            error_notif.setVibrate(new long[] { 500, 500 });

            //Create 'handshake captured' notification (used by wpa_thread)
            handshake_notif = new NotificationCompat.Builder(MainActivity.this);
            handshake_notif.setContentTitle(getString(R.string.handshake_captured));
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
                handshake_notif.setColor(getColor(android.R.color.holo_green_dark));
            }
            handshake_notif.setSmallIcon(R.drawable.ic_notification);
            handshake_notif.setContentIntent(click_intent);
            handshake_notif.setVibrate(new long[] { 500, 500 });

            //Load strings for when they cannot be retrieved with getString or R.string...
            publishProgress(getString(R.string.loading_strings));
            ST.not_connected = getString(R.string.not_connected);
            ST.paired = getString(R.string.paired) + ' ';

            //Setup tools
            publishProgress(getString(R.string.setting_up_tools));
            if (arch.equals("armv7l") || arch.equals("aarch64")) {
                File bin = new File(path + "/bin");
                File lib = new File(path + "/lib");
                if (!bin.exists()) {
                    if (!bin.mkdir()) {
                        publishProgress(null, getString(R.string.bin_not_created));
                        return false;
                    }
                }
                if (!lib.exists()) {
                    if (!lib.mkdir()) {
                        publishProgress(null, getString(R.string.lib_not_created));
                        return false;
                    }
                }
                boolean install = true;
                if (bin.list().length == 20 && lib.list().length == 1 && info != null) {
                    if (info.versionCode == pref.getInt("tools_version", 0)) {
                        if (debug)
                            Log.d("HIJACKER/SetupTask", "Tools already installed");
                        install = false;
                    } else {
                        File manufDB = new File(manufDBFile);
                        if (manufDB.exists())
                            manufDB.delete();
                    }
                }
                if (install) {
                    String tools_location = path + "/bin/";
                    String lib_location = path + "/lib/";
                    extract("airbase-ng", tools_location, true);
                    extract("aircrack-ng", tools_location, true);
                    extract("aireplay-ng", tools_location, true);
                    extract("airodump-ng", tools_location, true);
                    extract("besside-ng", tools_location, true);
                    extract("busybox", tools_location, true);
                    extract("ivstools", tools_location, true);
                    extract("iw", tools_location, true);
                    extract("iwconfig", tools_location, true);
                    extract("iwlist", tools_location, true);
                    extract("iwpriv", tools_location, true);
                    extract("kstats", tools_location, true);
                    extract("makeivs-ng", tools_location, true);
                    extract("mdk3", tools_location, true);
                    extract("nc", tools_location, true);
                    extract("packetforge-ng", tools_location, true);
                    extract("reaver", tools_location, true);
                    extract("reaver-wash", tools_location, true);
                    extract("wesside-ng", tools_location, true);
                    extract("wpaclean", tools_location, true);
                    extract("libfakeioctl.so", lib_location, true);

                    if (info != null) {
                        pref_edit.putInt("tools_version", info.versionCode);
                        pref_edit.apply();
                    }
                }
                busybox = path + "/bin/busybox";

                prefix = "LD_PRELOAD=" + path + "/lib/libfakeioctl.so";
                airodump_dir = path + "/bin/airodump-ng";
                aireplay_dir = path + "/bin/aireplay-ng";
                aircrack_dir = path + "/bin/aircrack-ng";
                mdk3_dir = path + "/bin/mdk3";
                reaver_dir = path + "/bin/reaver";
            } else {
                Log.e("HIJACKER/onCreate", "Device not armv7l or aarch64, can't install tools");
                busybox = "busybox";
                publishProgress(null, getString(R.string.not_armv7l));

                prefix = pref.getString("prefix", prefix);
                airodump_dir = "airodump-ng";
                aireplay_dir = "aireplay-ng";
                aircrack_dir = "aircrack-ng";
                mdk3_dir = "mdk3";
                reaver_dir = "reaver";
            }

            //Initialize RootFile
            publishProgress(getString(R.string.init_rootFile));
            RootFile.init();

            //Initialize threads
            publishProgress(getString(R.string.init_threads));
            wpa_runnable = new Runnable() {
                @Override
                public void run() {
                    if (debug)
                        Log.d("HIJACKER/wpa_thread", "Started wpa_thread");

                    Thread counter_thread = new Thread(new Runnable() {
                        @Override
                        public void run() {
                            if (debug)
                                Log.d("HIJACKER/wpa_subthread", "wpa_subthread started");
                            try {
                                progress_int = 0;
                                while (progress_int <= deauthWait && wpacheckcont) {
                                    Thread.sleep(1000);
                                    progress_int++;
                                    runInHandler(new Runnable() {
                                        @Override
                                        public void run() {
                                            progress.setProgress(progress_int);
                                        }
                                    });
                                }
                                if (wpacheckcont) {
                                    runInHandler(new Runnable() {
                                        @Override
                                        public void run() {
                                            if (!background)
                                                Snackbar.make(findViewById(R.id.fragment1),
                                                        getString(R.string.stopped_to_capture),
                                                        Snackbar.LENGTH_SHORT).show();
                                            else
                                                Toast.makeText(MainActivity.this,
                                                        getString(R.string.stopped_to_capture), Toast.LENGTH_SHORT)
                                                        .show();
                                            progress.setProgress(deauthWait);
                                            progress.setIndeterminate(true);
                                        }
                                    });
                                }
                            } catch (InterruptedException e) {
                                Log.e("HIJACKER/Exception", "Caught Exception in wpa_subthread: " + e.toString());
                                runInHandler(new Runnable() {
                                    @Override
                                    public void run() {
                                        progress.setIndeterminate(false);
                                        progress.setProgress(deauthWait);
                                    }
                                });
                            } finally {
                                stop(PROCESS_AIREPLAY);
                            }
                            if (debug)
                                Log.d("HIJACKER/wpa_subthread", "wpa_subthread finished");
                        }
                    });

                    boolean handshake_captured = false;
                    final String capfile = Airodump.getCapFile();
                    Shell shell = getFreeShell();
                    try {
                        if (capfile == null) {
                            if (debug)
                                Log.d("HIJACKER/wpa_thread",
                                        "cap file not found, airodump is probably not running...");
                        } else {
                            if (debug)
                                Log.d("HIJACKER/wpa_thread", capfile);
                            wpacheckcont = true;
                            counter_thread.start();

                            BufferedReader out = shell.getShell_out();
                            String buffer;
                            while (!handshake_captured && wpacheckcont) {
                                //Check loop
                                if (debug)
                                    Log.d("HIJACKER/wpa_thread", "Checking cap file...");
                                shell.run(aircrack_dir + " " + capfile + "; echo ENDOFAIR");
                                buffer = out.readLine();
                                if (buffer == null)
                                    break;
                                else {
                                    while (!buffer.equals("ENDOFAIR")) {
                                        if (buffer.length() >= 56) {
                                            if (buffer.charAt(56) == '1' || buffer.charAt(56) == '2'
                                                    || buffer.charAt(56) == '3') {
                                                handshake_captured = true;
                                                break;
                                            }
                                        }
                                        buffer = out.readLine();
                                    }
                                    Thread.sleep(700);
                                }
                            }
                        }
                    } catch (IOException | InterruptedException e) {
                        Log.e("HIJACKER/Exception", "Caught Exception in wpa_thread: " + e.toString());
                    } finally {
                        wpacheckcont = false;
                        counter_thread.interrupt();
                        shell.done();
                        final boolean found = handshake_captured;
                        if (found)
                            Airodump.startClean(is_ap);
                        runInHandler(new Runnable() {
                            @Override
                            public void run() {
                                Button crack_btn = (Button) findViewById(R.id.crack);
                                if (crack_btn != null) {
                                    //We are in IsolatedFragment
                                    crack_btn.setText(getString(R.string.crack));
                                }

                                if (found) {
                                    if (!background) {
                                        Snackbar s = Snackbar.make(findViewById(R.id.fragment1),
                                                getString(R.string.handshake_captured) + ' ' + capfile,
                                                Snackbar.LENGTH_LONG);
                                        s.setAction(R.string.crack, new View.OnClickListener() {
                                            @Override
                                            public void onClick(View v) {
                                                CrackFragment.capfile_text = capfile;
                                                FragmentTransaction ft = mFragmentManager.beginTransaction();
                                                ft.replace(R.id.fragment1, new CrackFragment());
                                                ft.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_OPEN);
                                                ft.addToBackStack(null);
                                                ft.commitAllowingStateLoss();
                                            }
                                        });
                                        s.show();
                                    } else {
                                        handshake_notif
                                                .setContentText(getString(R.string.saved_in_file) + ' ' + capfile);
                                        mNotificationManager.notify(2, handshake_notif.build());
                                    }
                                    progress.setIndeterminate(false);
                                }
                                if (debug)
                                    Log.d("HIJACKER/wpa_thread", "wpa_thread finished");
                            }
                        });
                    }
                }
            };
            wpa_thread = new Thread(wpa_runnable);

            //Start threads
            publishProgress(getString(R.string.starting_threads));
            new Thread(new Runnable() { //Thread to wait until the drawer is initialized and then highlight airodump
                @Override
                public void run() {
                    //Thread to wait for the drawer to be initialized, so that the first option (airodump) can be highlighted
                    try {
                        while (mDrawerList.getChildAt(0) == null) {
                            Thread.sleep(100);
                        }
                        runInHandler(new Runnable() {
                            @Override
                            public void run() {
                                refreshDrawer();
                            }
                        });
                    } catch (InterruptedException ignored) {
                    }
                }
            }).start();

            //Start background service so the app won't get killed if it goes to the background
            publishProgress(getString(R.string.starting_pers_service));
            startService(new Intent(MainActivity.this, PersistenceService.class));

            //Load manufacturer AVL tree
            publishProgress(getString(R.string.loading_manuf_db));
            manufHashMap = new HashMap<>();
            File db = new File(manufDBFile);
            if (!db.exists()) {
                //Database has not been created
                try {
                    //Create the database
                    if (!db.createNewFile()) {
                        Log.e("HIJACKER/SetupTask", "Error creating database file");
                    } else {
                        if (debug)
                            Log.d("HIJACKER/SetupTask", "Creating manufacturer database...");
                        publishProgress(getString(R.string.building_manuf_db));
                        BufferedReader out = new BufferedReader(
                                new InputStreamReader(getResources().getAssets().open("oui.txt")));
                        FileWriter in = new FileWriter(db);

                        //Load data from oui.txt and write to the new database file
                        String buffer = out.readLine();
                        while (buffer != null) {
                            if (buffer.length() < 18 || !buffer.contains("(base 16)")) {
                                buffer = out.readLine();
                                continue;
                            }

                            String mac = buffer.substring(0, 6);
                            String manuf = buffer.substring(22);
                            if (manufHashMap.get(mac) == null) {
                                //Write to file only if it was added to the AVL (it's unique)
                                manufHashMap.put(mac, manuf);
                                in.write(mac + ";" + manuf + '\n');
                            }

                            buffer = out.readLine();
                        }
                        in.close();
                        out.close();
                        if (debug)
                            Log.d("HIJACKER/SetupTask", "Manufacturer database built");
                    }
                } catch (IOException e) {
                    Log.e("HIJACKER/SetupTask", e.toString());
                    manufHashMap = null;
                }
            } else {
                //Load database on the AVL
                try {
                    //Database format:
                    //01B256;Manufacturer co.\n
                    BufferedReader out = new BufferedReader(new FileReader(db));

                    String buffer = out.readLine();
                    while (buffer != null) {
                        String mac = buffer.substring(0, buffer.indexOf(';'));
                        String manuf = buffer.substring(buffer.indexOf(';') + 1);
                        manufHashMap.put(mac, manuf);
                        buffer = out.readLine();
                    }
                } catch (IOException e) {
                    Log.e("HIJACKER/SetupTask", e.toString());
                    manufHashMap = null;
                }
            }

            if (ContextCompat.checkSelfPermission(MainActivity.this,
                    Manifest.permission.READ_EXTERNAL_STORAGE) == PackageManager.PERMISSION_GRANTED) {
                //Load custom actions
                publishProgress(getString(R.string.loading_custom_actions));
                CustomAction.load();

                //Create or read aliases file
                publishProgress(getString(R.string.loading_aliases));
                loadAliases();
            }

            //Checking permissions
            publishProgress(getString(R.string.checking_permissions));
            ActivityCompat.requestPermissions(MainActivity.this,
                    new String[] { Manifest.permission.CHANGE_WIFI_STATE, Manifest.permission.ACCESS_WIFI_STATE,
                            Manifest.permission.WRITE_EXTERNAL_STORAGE, Manifest.permission.READ_EXTERNAL_STORAGE,
                            Manifest.permission.ACCESS_NETWORK_STATE, Manifest.permission.INTERNET },
                    0);

            //Check for updates
            if (update_on_startup) {
                if (internetAvailable(MainActivity.this)) {
                    publishProgress(getString(R.string.checking_for_updates));
                    checkForUpdate(MainActivity.this, false);
                } else {
                    //Spawn new thread to wait for internet connection
                    //This should be changed to a broadcast receiver
                    new Thread(new Runnable() {
                        @Override
                        public void run() {
                            try {
                                while (!internetAvailable(MainActivity.this)) {
                                    Thread.sleep(1000);
                                }
                                checkForUpdate(MainActivity.this, false);
                            } catch (InterruptedException ignored) {
                            }
                        }
                    }).start();
                }
            }

            //Delete old report, it's not needed if no exception is thrown up to this point
            publishProgress(getString(R.string.deleting_bug_report));
            File report = new File(Environment.getExternalStorageDirectory() + "/report.txt");
            if (report.exists())
                report.delete();

            return true;
        }

        @Override
        protected void onProgressUpdate(String... progress) {
            if (progress[0] != null) {
                //Update loading dialog with progress[0]
                loadingDialog.setText(progress[0]);
            } else if (progress[1] != null) {
                //Show error message with progress[1]
                ErrorDialog dialog = new ErrorDialog();
                dialog.setMessage(progress[1]);
                dialog.show(getFragmentManager(), "ErrorDialog");
            }
        }

        @Override
        protected void onPostExecute(final Boolean success) {
            if (!success)
                return;
            loadingDialog.setText(getString(R.string.starting_hijacker));

            watchdogTask = new WatchdogTask(MainActivity.this);
            if (watchdog) {
                watchdogTask.execute();
            }

            //Load default fragment (airodump)
            if (mFragmentManager.getBackStackEntryCount() == 0) {
                FragmentTransaction ft = mFragmentManager.beginTransaction();
                ft.replace(R.id.fragment1, new MyListFragment());
                ft.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_OPEN);
                ft.addToBackStack(null);
                ft.commitAllowingStateLoss();
            }

            loadingDialog.dismissAllowingStateLoss();

            //Start
            if (!pref.getBoolean("disclaimer", false)) {
                onResumeRunnable = new Runnable() {
                    @Override
                    public void run() {
                        //First start
                        new DisclaimerDialog().show(getFragmentManager(), "Disclaimer");
                        //Check for SuperSU
                        if (!new File("/su").exists()) {
                            ErrorDialog dialog = new ErrorDialog();
                            dialog.setTitle(getString(R.string.su_notfound_title));
                            dialog.setMessage(getString(R.string.su_notfound));
                            dialog.show(getFragmentManager(), "ErrorDialog");
                        }
                        onResumeRunnable = null;
                    }
                };
                if (!background) {
                    onResumeRunnable.run();
                }
            } else
                main();
        }
    }

    void extract(String filename, String out_dir, boolean chmod) {
        File f = new File(out_dir, filename);
        if (f.exists())
            f.delete(); //Delete file in case it's outdated
        try {
            InputStream in = getResources().getAssets().open(filename);
            FileOutputStream out = new FileOutputStream(f);

            byte[] buf = new byte[BUFFER_SIZE];
            int len;
            while ((len = in.read(buf)) > 0) {
                out.write(buf, 0, len);
            }
            in.close();
            out.close();
            if (chmod) {
                runOne("chmod 755 " + out_dir + "/" + filename);
            }
        } catch (IOException e) {
            Log.e("HIJACKER/FileProvider", "Exception copying from assets", e);
        }
    }

    public void main() {
        runOne(enable_monMode);
        runOne("mkdir " + cap_dir);

        stop(PROCESS_AIRODUMP);
        stop(PROCESS_AIREPLAY);
        stop(PROCESS_MDK);
        stop(PROCESS_AIRCRACK);
        stop(PROCESS_REAVER);
        if (airOnStartup)
            Airodump.startClean();
    }

    public static void _startAireplay(final String str) {
        try {
            String cmd = "su -c " + prefix + " " + aireplay_dir + " -D --ignore-negative-one " + str + " " + iface;
            if (debug)
                Log.d("HIJACKER/_startAireplay", cmd);
            Runtime.getRuntime().exec(cmd);
            last_action = System.currentTimeMillis();
        } catch (IOException e) {
            Log.e("HIJACKER/Exception", "Caught Exception in _startAireplay() start block: " + e.toString());
        }
        runInHandler(new Runnable() {
            @Override
            public void run() {
                menu.getItem(3).setEnabled(true); //Enable 'Stop aireplay' button
                refreshState();
                notification();
            }
        });
    }

    public static void startAireplay(String mac) {
        //Disconnect all clients from mac
        aireplay_running = AIREPLAY_DEAUTH;
        _startAireplay("--deauth 0 -a " + mac);
    }

    public static void startAireplay(String target, String client) {
        //Disconnect client client from ap target
        aireplay_running = AIREPLAY_DEAUTH;
        _startAireplay("--deauth 0 -a " + target + " -c " + client);
    }

    public static void startAireplayWEP(AP ap) {
        //Increase IV generation from ap mac to crack a wep network
        aireplay_running = AIREPLAY_WEP;
        _startAireplay("--fakeauth 0 -a " + ap.mac + " -e " + ap.essid);
        //_startAireplay("--arpreplay -b " + ap.mac);       //Aireplay tries to open a file at a read-only system
        //_startAireplay("--caffe-latte -b " + ap.mac);     //don't know where
    }

    public static void startMdk(int mode, String str) {
        ArrayList<Integer> ps_before = getPIDs(PROCESS_MDK);
        switch (mode) {
        case MDK_BF:
            //beacon flood mode
            try {
                String cmd = "su -c " + prefix + " " + mdk3_dir + " " + iface + " b -m ";
                if (str != null)
                    cmd += str;
                if (debug)
                    Log.d("HIJACKER/MDK3", cmd);
                Runtime.getRuntime().exec(cmd);
                last_action = System.currentTimeMillis();
                Thread.sleep(500);
            } catch (IOException | InterruptedException e) {
                Log.e("HIJACKER/Exception", "Caught Exception in startMdk(MDK_BF) start block: " + e.toString());
            }
            bf = true;
            break;
        case MDK_ADOS:
            //Authentication DoS mode
            try {
                String cmd = "su -c " + prefix + " " + mdk3_dir + " " + iface + " a -m";
                cmd += str == null ? "" : " -i " + str;
                if (debug)
                    Log.d("HIJACKER/MDK3", cmd);
                Runtime.getRuntime().exec(cmd);
                last_action = System.currentTimeMillis();
                Thread.sleep(500);
            } catch (IOException | InterruptedException e) {
                Log.e("HIJACKER/Exception", "Caught Exception in startMdk(MDK_ADOS) start block: " + e.toString());
            }
            ados = true;
            break;
        }
        ArrayList<Integer> ps_after = getPIDs(PROCESS_MDK);
        int pid = 0;
        if (ps_before.size() != ps_after.size()) {
            for (int i = 0; i < ps_before.size(); i++) {
                if (!ps_before.get(i).equals(ps_after.get(i))) {
                    pid = ps_after.get(i);
                }
            }
            if (pid == 0)
                pid = ps_after.get(ps_after.size() - 1);
        }
        switch (mode) {
        case MDK_BF:
            bf_pid = pid;
            break;
        case MDK_ADOS:
            ados_pid = pid;
            break;
        }
        runInHandler(new Runnable() {
            @Override
            public void run() {
                refreshState();
                notification();
            }
        });
    }

    public static ArrayList<Integer> getPIDs(String process_name) {
        if (process_name == null)
            return null;

        Shell shell = getFreeShell();
        ArrayList<Integer> list = new ArrayList<>();
        shell.run(busybox + " pidof " + process_name + "; echo ENDOFPIDOF");
        BufferedReader out = shell.getShell_out();
        String buffer = null;
        try {
            while (buffer == null)
                buffer = out.readLine();
            while (!buffer.equals("ENDOFPIDOF")) {
                String[] temp = buffer.split(" ");
                try {
                    for (String tmp : temp) {
                        list.add(Integer.parseInt(tmp));
                    }
                } catch (NumberFormatException e) {
                    Log.e("HIJACKER/getPIDs", "Exception: " + e.toString());
                }
                buffer = out.readLine();
            }
        } catch (IOException e) {
            Log.e("HIJACKER/getPIDs", "Exception: " + e.toString());
            list = null;
        }
        shell.done();
        return list;
    }

    public static ArrayList<Integer> getPIDs(int pr) {
        switch (pr) {
        case PROCESS_AIRODUMP:
            return getPIDs("airodump-ng");
        case PROCESS_AIREPLAY:
            return getPIDs("aireplay-ng");
        case PROCESS_MDK:
            return getPIDs("mdk3");
        case PROCESS_AIRCRACK:
            return getPIDs("aircrack-ng");
        case PROCESS_REAVER:
            return getPIDs("reaver");
        default:
            Log.e("HIJACKER/getPIDs", "Method called with invalid pr code");
            throw new UnsupportedOperationException("getPIDs() called with invalid pr code");
        }
    }

    public static void stop(int pr) {
        //0 for airodump-ng, 1 for aireplay-ng, 2 for mdk, 3 for aircrack, 4 for reaver, everything else is considered pid and we kill it
        if (debug)
            Log.d("HIJACKER/stop", "stop(" + pr + ") called");
        last_action = System.currentTimeMillis();
        switch (pr) {
        case PROCESS_AIRODUMP:
            Airodump.stop();
            return;
        case PROCESS_AIREPLAY:
            runInHandler(new Runnable() {
                @Override
                public void run() {
                    if (menu != null)
                        menu.getItem(3).setEnabled(false);
                }
            });
            progress_int = deauthWait;
            runOne(busybox + " kill $(" + busybox + " pidof aireplay-ng)");
            if (is_ap == null && aireplay_running == AIREPLAY_DEAUTH && Airodump.isRunning()) {
                //Aireplay was just deauthenticating so airodump has locked a channel, no more needed
                Airodump.startClean();
            }
            AP.currentTargetDeauth.clear();
            aireplay_running = 0;
            break;
        case PROCESS_MDK:
            if (currentFragment == FRAGMENT_MDK) {
                runInHandler(new Runnable() {
                    @Override
                    public void run() {
                        MDKFragment.bf_switch.setChecked(false);
                        MDKFragment.ados_switch.setChecked(false);
                    }
                });
            }
            ados = false;
            bf = false;
            runOne(busybox + " kill $(" + busybox + " pidof mdk3)");
            break;
        case PROCESS_AIRCRACK:
            CrackFragment.cont = false;
            runOne(busybox + " kill $(" + busybox + " pidof aircrack-ng)");
            break;
        case PROCESS_REAVER:
            ReaverFragment.stopReaver();
            runOne(busybox + " kill $(" + busybox + " pidof reaver)");
            break;
        default:
            runOne(busybox + " kill " + pr);
            break;
        }
        runInHandler(new Runnable() {
            @Override
            public void run() {
                refreshState();
                notification();
            }
        });
    }

    public static void stopWPA() {
        wpacheckcont = false;
        wpa_thread.interrupt();
    }

    public static Handler handler = new Handler();

    public static void runInHandler(Runnable runnable) {
        handler.post(runnable);
    }

    static void load() {
        //Load Preferences
        Log.d("HIJACKER/load", "Loading preferences...");

        iface = pref.getString("iface", iface);
        if (!(arch.equals("armv7l") || arch.equals("aarch64"))) {
            prefix = pref.getString("prefix", prefix);
        }
        deauthWait = Integer.parseInt(pref.getString("deauthWait", Integer.toString(deauthWait)));
        chroot_dir = pref.getString("chroot_dir", chroot_dir);
        monstart = pref.getBoolean("monstart", monstart);
        cap_dir = pref.getString("cap_dir", cap_dir);
        enable_monMode = pref.getString("enable_monMode", enable_monMode);
        disable_monMode = pref.getString("disable_monMode", disable_monMode);
        enable_on_airodump = pref.getBoolean("enable_on_airodump", enable_on_airodump);
        show_notif = pref.getBoolean("show_notif", show_notif);
        show_details = pref.getBoolean("show_details", show_details);
        airOnStartup = pref.getBoolean("airOnStartup", airOnStartup);
        debug = pref.getBoolean("debug", debug);
        watchdog = pref.getBoolean("watchdog", watchdog);
        target_deauth = pref.getBoolean("target_deauth", target_deauth);
        delete_extra = pref.getBoolean("delete_extra", delete_extra);
        try {
            always_cap = pref.getBoolean("always_cap", always_cap);
        } catch (ClassCastException e) {
            pref_edit.putBoolean("always_cap", false);
            pref_edit.commit();
        }
        custom_chroot_cmd = pref.getString("custom_chroot_cmd", custom_chroot_cmd);
        cont_on_fail = pref.getBoolean("cont_on_fail", cont_on_fail);
        update_on_startup = pref.getBoolean("update_on_startup", update_on_startup);
        progress.setMax(deauthWait);
        progress.setProgress(deauthWait);
    }

    static void loadAliases() {
        aliases_file = new File(data_path + "/aliases.txt");
        try {
            if (!aliases_file.exists()) {
                aliases_file.createNewFile();
            } else {
                if (debug)
                    Log.d("HIJACKER/loadAliases", "Reading aliases file...");
                try {
                    BufferedReader aliases_out = new BufferedReader(new FileReader(aliases_file));
                    String buffer = aliases_out.readLine();
                    while (buffer != null) {
                        //Line format: 00:11:22:33:44:55 Alias
                        if (buffer.charAt(17) == ' ' && buffer.length() > 18) {
                            String mac = buffer.substring(0, 17);
                            String alias = buffer.substring(18);
                            aliases.put(mac, alias);
                        } else {
                            Log.e("HIJACKER/loadAliases", "Aliases file format error: " + buffer);
                        }
                        buffer = aliases_out.readLine();
                    }
                    aliases_out.close();
                } catch (IOException e) {
                    Log.e("HIJACKER/loadAliases", e.toString());
                }
            }
            aliases_in = new FileWriter(aliases_file, true);
        } catch (IOException e) {
            Log.e("HIJACKER/loadAliases", e.toString());
            aliases_in = null;
        }
    }

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        switch (item.getItemId()) {
        case R.id.reset:
            stop(PROCESS_AIRODUMP);
            Tile.clear();
            Tile.onCountsChanged();
            Airodump.startClean();
            return true;

        case R.id.stop_run:
            if (Airodump.isRunning())
                stop(PROCESS_AIRODUMP);
            else
                Airodump.start();
            return true;

        case R.id.stop_aireplay:
            stop(PROCESS_AIREPLAY);
            return true;

        case R.id.filter:
            new FiltersDialog().show(mFragmentManager, "FiltersDialog");
            return true;

        case R.id.settings:
            if (currentFragment != FRAGMENT_SETTINGS) {
                FragmentTransaction ft = mFragmentManager.beginTransaction();
                ft.replace(R.id.fragment1, new SettingsFragment());
                ft.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_OPEN);
                ft.addToBackStack(null);
                ft.commitAllowingStateLoss();
            }
            return true;

        case R.id.export:
            new ExportDialog().show(mFragmentManager, "ExportDialog");
            return true;

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

    @Override
    protected void onDestroy() {
        notif_on = false;
        mNotificationManager.cancelAll();
        CustomAction.save();
        watchdogTask.cancel(true);
        stop(PROCESS_AIRODUMP);
        stop(PROCESS_AIREPLAY);
        stop(PROCESS_MDK);
        stop(PROCESS_AIRCRACK);
        stop(PROCESS_REAVER);
        runOne(disable_monMode);
        RootFile.finish();
        Shell.exitAll();
        stopService(new Intent(this, PersistenceService.class));
        super.onDestroy();
        System.exit(0);
    }

    @Override
    protected void onResume() {
        super.onResume();
        notif_on = false;
        background = false;
        if (mNotificationManager != null)
            mNotificationManager.cancelAll();
        if (onResumeRunnable != null) {
            onResumeRunnable.run();
        }
    }

    @Override
    protected void onStop() {
        super.onStop();
        AppIndex.AppIndexApi.end(client, getIndexApiAction());
        background = true;
        if (show_notif) {
            notif_on = true;
            notification();
        }
        client.disconnect();
    }

    @Override
    public boolean onKeyDown(int keyCode, KeyEvent event) {
        if (keyCode == KeyEvent.KEYCODE_BACK) {
            if (mDrawerLayout.isDrawerOpen(mDrawerList)) {
                mDrawerLayout.closeDrawer(mDrawerList);
            } else if (mFragmentManager.getBackStackEntryCount() > 1) {
                mFragmentManager.popBackStackImmediate();
            } else {
                new ExitDialog().show(mFragmentManager, "ExitDialog");
            }
            return true;
        }
        return super.onKeyDown(keyCode, event);
    }

    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        MainActivity.menu = menu;
        getMenuInflater().inflate(R.menu.toolbar, menu);
        if (airOnStartup) {
            menu.getItem(1).setIcon(R.drawable.stop_drawable);
            menu.getItem(1).setTitle(R.string.stop);
        }
        menu.getItem(3).setEnabled(false);
        return true;
    }

    @Override
    public void onRequestPermissionsResult(int requestCode, @NonNull String permissions[],
            @NonNull int[] grantResults) {
        if (requestCode == 0) {
            //The one and only request this app sends
            if (grantResults.length > 0 && grantResults[2] == PackageManager.PERMISSION_GRANTED) {
                CustomAction.load();
                loadAliases();
            }
        }
    }

    // See https://g.co/AppIndexing/AndroidStudio for more information.
    @Override
    public void onStart() {
        super.onStart();
        client.connect();
        AppIndex.AppIndexApi.start(client, getIndexApiAction());
    }

    public Action getIndexApiAction() {
        Thing object = new Thing.Builder().setName("AirodumpGUI")
                .setUrl(Uri.parse("https://github.com/chrisk44/Hijacker")).build();
        return new Action.Builder(Action.TYPE_VIEW).setObject(object).setActionStatus(Action.STATUS_TYPE_COMPLETED)
                .build();
    }

    private class DrawerItemClickListener implements ListView.OnItemClickListener {
        @Override
        public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
            if (currentFragment != position) {
                FragmentTransaction ft = mFragmentManager.beginTransaction();
                switch (position) {
                case FRAGMENT_AIRODUMP:
                    ft.replace(R.id.fragment1, is_ap == null ? new MyListFragment() : new IsolatedFragment());
                    break;
                case FRAGMENT_MDK:
                    ft.replace(R.id.fragment1, new MDKFragment());
                    break;
                case FRAGMENT_CRACK:
                    ft.replace(R.id.fragment1, new CrackFragment());
                    break;
                case FRAGMENT_REAVER:
                    ft.replace(R.id.fragment1, new ReaverFragment());
                    break;
                case FRAGMENT_CUSTOM:
                    ft.replace(R.id.fragment1, new CustomActionFragment());
                    break;
                case FRAGMENT_SETTINGS:
                    ft.replace(R.id.fragment1, new SettingsFragment());
                    break;
                }
                ft.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_OPEN);
                ft.addToBackStack(null);
                ft.commitAllowingStateLoss();
                mFragmentManager.executePendingTransactions();
            }
            mDrawerLayout.closeDrawer(mDrawerList);
        }
    }

    class MyListAdapter extends ArrayAdapter<Tile> {
        MyListAdapter() {
            super(MainActivity.this, R.layout.listitem);
        }

        @NonNull
        @Override
        public View getView(int position, View convertView, @NonNull ViewGroup parent) {
            // get a view to work with
            View itemview = convertView;
            if (itemview == null) {
                itemview = getLayoutInflater().inflate(R.layout.listitem, parent, false);
            }

            // find the item to work with
            Tile current = Tile.tiles.get(position);

            TextView upperLeft = (TextView) itemview.findViewById(R.id.upperLeft);
            upperLeft.setText(current.device.upperLeft);
            upperLeft.setTextColor(ContextCompat.getColor(getContext(),
                    current.device.isMarked ? R.color.colorAccent : android.R.color.white));

            TextView lowerLeft = (TextView) itemview.findViewById(R.id.lowerLeft);
            lowerLeft.setText(current.device.lowerLeft);

            TextView lowerRight = (TextView) itemview.findViewById(R.id.lowerRight);
            lowerRight.setText(current.device.lowerRight);

            TextView upperRight = (TextView) itemview.findViewById(R.id.upperRight);
            upperRight.setText(current.device.upperRight);

            //Image
            ImageView iv = (ImageView) itemview.findViewById(R.id.iv);
            if (current.device instanceof AP) {
                if (((AP) current.device).isHidden)
                    iv.setImageResource(R.drawable.ap_hidden);
                else
                    iv.setImageResource(R.drawable.ap2);
            } else {
                iv.setImageResource(R.drawable.st2);
            }

            return itemview;
        }

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

    class CustomActionAdapter extends ArrayAdapter<CustomAction> {
        CustomActionAdapter() {
            super(MainActivity.this, R.layout.listitem);
        }

        @NonNull
        @Override
        public View getView(int position, View convertView, @NonNull ViewGroup parent) {
            // get a view to work with
            View itemview = convertView;
            if (itemview == null) {
                itemview = getLayoutInflater().inflate(R.layout.listitem, parent, false);
            }

            // find the item to work with
            CustomAction currentItem = CustomAction.cmds.get(position);

            TextView upperLeft = (TextView) itemview.findViewById(R.id.upperLeft);
            upperLeft.setText(currentItem.getTitle());

            TextView lowerLeft = (TextView) itemview.findViewById(R.id.lowerLeft);
            lowerLeft.setText(currentItem.getStartCmd());

            TextView lowerRight = (TextView) itemview.findViewById(R.id.lowerRight);
            lowerRight.setText("");

            TextView upperRight = (TextView) itemview.findViewById(R.id.upperRight);
            upperRight.setText("");

            //Image
            ImageView iv = (ImageView) itemview.findViewById(R.id.iv);
            if (currentItem.getType() == TYPE_ST) {
                iv.setImageResource(R.drawable.st2);
            } else {
                iv.setImageResource(R.drawable.ap2);
            }

            return itemview;
        }

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

    class FileExplorerAdapter extends ArrayAdapter<RootFile> {
        FileExplorerAdapter() {
            super(MainActivity.this, R.layout.explorer_item);
        }

        @NonNull
        @Override
        public View getView(int position, View convertView, @NonNull ViewGroup parent) {
            // get a view to work with
            View itemview = convertView;
            if (itemview == null) {
                itemview = getLayoutInflater().inflate(R.layout.explorer_item, parent, false);
            }

            // find the item to work with
            RootFile currentItem = FileExplorerDialog.list.get(position);

            TextView firstText = (TextView) itemview.findViewById(R.id.explorer_item_tv);
            firstText.setText(currentItem.getName());

            //Image
            ImageView iv = (ImageView) itemview.findViewById(R.id.explorer_iv);
            if (currentItem.isFile()) {
                iv.setImageResource(R.drawable.file);
            } else {
                iv.setImageResource(R.drawable.folder);
            }

            return itemview;
        }

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

    public void onAPStats(View v) {
        new StatsDialog().show(mFragmentManager, "StatsDialog");
    }

    public void onCrack(View v) {
        //Clicked crack with isolated ap
        if (wpa_thread.isAlive()) {
            //Clicked stop
            stopWPA();
            Airodump.startClean(is_ap);
        } else {
            //Clicked crack
            is_ap.crack();
            ((TextView) v).setText(R.string.stop);
        }
    }

    public void onDisconnect(View v) {
        //Clicked disconnect all with isolated ap
        stop(PROCESS_AIREPLAY);
        startAireplay(is_ap.mac);
        Toast.makeText(this, R.string.disconnect_started, Toast.LENGTH_SHORT).show();
    }

    public void onDos(View v) {
        //Clicked dos with isolated ap
        if (ados) {
            ((TextView) v).setText(R.string.dos);
            stop(ados_pid);
            ados = false;
        } else {
            ((TextView) v).setText(R.string.stop);
            startMdk(MDK_ADOS, is_ap.mac);
        }
    }

    public void onCopy(View v) {
        copy(((TextView) v).getText().toString(), v);
    }

    static void copy(String str, View view) {
        clipboard.setPrimaryClip(ClipData.newPlainText("label", str));
        if (view != null)
            Snackbar.make(view, "\"" + str + "\" copied to clipboard", Snackbar.LENGTH_SHORT).show();
    }

    static void notification() {
        if (notif_on && show_notif) {
            if (show_details) {
                String str;
                if (is_ap == null)
                    str = "APs: " + Tile.i + " | STs: " + (Tile.tiles.size() - Tile.i);
                else
                    str = is_ap.essid + " | STs: " + (Tile.tiles.size() - Tile.i);

                if (aireplay_running == AIREPLAY_DEAUTH)
                    str += " | Aireplay deauthenticating...";
                else if (aireplay_running == AIREPLAY_WEP)
                    str += " | Aireplay replaying for wep...";
                if (wpa_thread != null) {
                    if (wpa_thread.isAlive())
                        str += " | WPA cracking...";
                }
                if (bf)
                    str += " | MDK3 Beacon Flooding...";
                if (ados)
                    str += " | MDK3 Authentication DoS...";
                if (ReaverFragment.isRunning())
                    str += " | Reaver running...";
                if (CrackFragment.cont)
                    str += " | Cracking .cap file...";
                if (CustomActionFragment.cont)
                    str += " | Running action " + CustomActionFragment.selected_action.getTitle() + "...";

                notif.setContentText(str);
            } else
                notif.setContentText(null);
            mNotificationManager.notify(0, notif.build());
        } else {
            mNotificationManager.cancel(0);
        }
    }

    static void isolate(String mac) {
        is_ap = getAPByMac(mac);
        if (is_ap != null) {
            IsolatedFragment.exit_on = mFragmentManager.getBackStackEntryCount();
            FragmentTransaction ft = mFragmentManager.beginTransaction();
            ft.replace(R.id.fragment1, new IsolatedFragment());
            ft.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_OPEN);
            ft.addToBackStack(null);
            ft.commitAllowingStateLoss();
        }
        Tile.filter();
        if (debug) {
            if (is_ap == null)
                Log.d("HIJACKER/Main", "No AP isolated");
            else
                Log.d("HIJACKER/Main", "AP with MAC " + mac + " isolated");
        }
    }

    static void refreshState() {
        //refresh overflow icon to show what is running
        int state = Airodump.isRunning() ? 1 : 0;
        if (aireplay_running != 0)
            state += 2;
        if (bf || ados)
            state += 4;
        toolbar.setOverflowIcon(overflow[state]);

        if (!(ReaverFragment.isRunning() || CrackFragment.cont || wpa_thread.isAlive())) {
            progress.setIndeterminate(false);
            progress.setProgress(deauthWait);
        }
    }

    static void refreshDrawer() {
        if (mDrawerList.getChildAt(0) != null) { //Ensure that the Drawer is initialized
            for (int i = 0; i < 6; i++) {
                mDrawerList.getChildAt(i).setBackgroundResource(R.color.colorPrimary);
            }
            mDrawerList.getChildAt(currentFragment).setBackgroundResource(R.color.colorAccent);
        }
        actionBar.setTitle(mPlanetTitles[currentFragment]);
    }

    static String getManuf(String mac) {
        mac = trimMac(mac);
        if (manufHashMap == null)
            return "Unknown Manufacturer";
        String manuf = manufHashMap.get(mac);
        if (manuf == null)
            return "Unknown Manufacturer";
        else
            return manuf;
    }

    static String getLastLine(BufferedReader out, String end) {
        //Returns the last line printed in out BEFORE end. If no other line is present, end is returned.
        String lastline = null, buffer = null;
        try {
            while (buffer == null)
                buffer = out.readLine();
            lastline = buffer;
            while (!end.equals(buffer) && buffer != null) {
                lastline = buffer;
                buffer = out.readLine();
            }
        } catch (IOException e) {
            Log.e("HIJACKER/Exception", "Exception in getLastLine: " + e);
        }

        return lastline;
    }

    static String getLastSeen(long lastseen) {
        String str = "";
        long diff = System.currentTimeMillis() - lastseen;
        if (diff < 1000)
            return "Just now";
        diff = diff / 1000; //diff is now seconds
        if (diff / 60 > 0) {
            //minutes = diff/60
            str += diff / 60 + " minute" + (diff / 60 > 1 ? "s " : " ");
            diff = diff % 60;
        }
        if (diff > 0) {
            str += diff + " second" + (diff > 1 ? "s " : " ");
        }
        str += "ago";
        return str;
    }

    static String getDirectory(String str) {
        //Returns a directory that ends with /
        if (str == null)
            return null;
        if (str.length() == 0)
            return str;

        if (str.charAt(str.length() - 1) == '/') {
            return str;
        } else
            return str + '/';
    }

    static String getFixed(String text, int size) {
        /*Returns a string of fixed length (size) that contains spaces followed by a text
           <--  size  -->
          |     ...  text|
        */
        if (text == null)
            return null;
        if (text.length() > size) {
            text = text.substring(0, size);
        }
        String str = "";
        for (int i = 0; i < size - text.length(); i++) {
            str += " ";
        }
        return str + text;
    }

    static int checkChroot() {
        boolean bin = false, dir;

        Shell shell = getFreeShell();
        shell.run("echo $PATH; echo ENDOFPATH");
        String path = getLastLine(shell.getShell_out(), "ENDOFPATH");
        shell.done();
        String paths[] = path.split(":");
        for (String temp : paths) {
            if (new RootFile(temp + "/bootkali_init").exists()) {
                bin = true;
                bootkali_init_bin = temp + "/bootkali_init";
                break;
            }
        }
        if (!bin) {
            if (new RootFile(NETHUNTER_BOOTKALI_BASH).exists()) {
                bin = true;
                bootkali_init_bin = NETHUNTER_BOOTKALI_BASH;
            }
        }

        dir = new RootFile(chroot_dir).exists() && new RootFile(chroot_dir + "/bin/bash").exists();

        if (bin && dir)
            return CHROOT_FOUND;
        else if (!bin && !dir)
            return CHROOT_BOTH_MISSING;
        else if (dir)
            return CHROOT_BIN_MISSING;
        else
            return CHROOT_DIR_MISSING;
    }

    static Socket connect() {
        //Can be called from any thread, blocks until the job is finished
        Socket socket;
        try {
            InetAddress ip = Inet4Address.getByName(SERVER);
            socket = new Socket();
            socket.connect(new InetSocketAddress(ip, PORT), 2000);

            PrintWriter in = new PrintWriter(socket.getOutputStream());
            BufferedReader out = new BufferedReader(new InputStreamReader(socket.getInputStream()));

            //Authenticate (receive a string generated by the server, combine it with a stored key and send back the hashcode)
            if (debug)
                Log.d("HIJACKER/connect", "Authenticating with server...");
            String temp = out.readLine() + AUTH_KEY;
            in.print(Integer.toString(temp.hashCode()) + '\n');
            in.flush();

            temp = out.readLine();
            if (temp != null) {
                if (!temp.equals(ANS_POSITIVE))
                    return null; //Not authenticated, socket will be closed by the server
            } else
                return null; //Connection closed, probably not authenticated
            if (debug)
                Log.d("HIJACKER/connect", "Authenticated");

            if (deviceID == -1) {
                if (debug)
                    Log.d("HIJACKER/connect", "Getting new deviceID...");
                in.print(REQ_NEW_ID + '\n');
                in.flush();

                try {
                    deviceID = Long.parseLong(out.readLine());
                    if (pref_edit != null) {
                        pref_edit.putLong("deviceID", deviceID);
                        pref_edit.commit();
                    }
                    if (debug)
                        Log.d("HIJACKER/connect", "New deviceID is " + deviceID);
                } catch (NumberFormatException ignored) {
                    if (debug)
                        Log.d("HIJACKER/connect", "deviceID caused NumberFormatException, still -1");
                }
            }
            //String should be: info APP_VERSION_NAME APP_VERSION_CODE ANDROID_VERSION DEVICE_MODEL DEVICE_ID
            in.print(REQ_INFO + " " + versionName + " " + versionCode + " " + Build.VERSION.SDK_INT + " "
                    + deviceModel + " " + deviceID + '\n');
            in.flush();
        } catch (IOException e) {
            Log.e("HIJACKER/connect", e.toString());
            return null;
        }
        return socket;
    }

    static void checkForUpdate(final Activity activity, final boolean showMessages) {
        //Can be called from any thread, blocks until the job is finished
        Runnable runnable = new Runnable() {
            @Override
            public void run() {
                progress.setIndeterminate(false);
            }
        };
        if (showMessages) {
            runInHandler(new Runnable() {
                @Override
                public void run() {
                    progress.setIndeterminate(true);
                }
            });
        }
        Socket socket = connect();
        if (socket == null) {
            if (showMessages) {
                runInHandler(runnable);
                Snackbar.make(rootView, activity.getString(R.string.server_error), Snackbar.LENGTH_SHORT).show();
            }
            return;
        }

        try {
            PrintWriter in = new PrintWriter(socket.getOutputStream());
            BufferedReader out = new BufferedReader(new InputStreamReader(socket.getInputStream()));

            in.print(REQ_VERSION + '\n');
            in.flush();

            int latestCode = Integer.parseInt(out.readLine());
            String latestName = out.readLine();
            String latestLink = out.readLine();

            in.print(REQ_EXIT + '\n');
            in.flush();
            in.close();
            out.close();
            socket.close();

            if (latestCode > versionCode) {
                final UpdateConfirmDialog dialog = new UpdateConfirmDialog();
                dialog.newVersionCode = latestCode;
                dialog.newVersionName = latestName;
                dialog.link = latestLink;
                runInHandler(new Runnable() {
                    @Override
                    public void run() {
                        dialog.show(activity.getFragmentManager(), "UpdateConfirmDialog");
                    }
                });
            } else {
                if (showMessages)
                    Snackbar.make(rootView, activity.getString(R.string.already_on_latest), Snackbar.LENGTH_SHORT)
                            .show();
            }
        } catch (IOException | NumberFormatException e) {
            Log.e("HIJACKER/update", e.toString());
            if (showMessages)
                Snackbar.make(rootView, activity.getString(R.string.unknown_error), Snackbar.LENGTH_SHORT).show();
        } finally {
            if (showMessages)
                runInHandler(runnable);
        }
    }

    static boolean internetAvailable(Context context) {
        ConnectivityManager connectivityManager = (ConnectivityManager) context
                .getSystemService(Context.CONNECTIVITY_SERVICE);
        return connectivityManager.getNetworkInfo(1).getState() == NetworkInfo.State.CONNECTED
                || connectivityManager.getNetworkInfo(0).getState() == NetworkInfo.State.CONNECTED;
    }

    static boolean createReport(File out, String filesDir, String stackTrace, Process shell) {
        if (!out.exists()) {
            try {
                if (!out.createNewFile())
                    return false;
            } catch (IOException e) {
                Log.e("HIJACKER/createReport", e.toString());
                return false;
            }
        }
        String busybox_tmp = filesDir + "/bin/busybox";
        PrintWriter shell_in = new PrintWriter(shell.getOutputStream());
        BufferedReader shell_out = new BufferedReader(new InputStreamReader(shell.getInputStream()));

        FileWriter writer = null;
        try {
            writer = new FileWriter(out, true);
            writer.write("\n--------------------------------------------------------------------------------\n");
            writer.write("Hijacker report - " + new Date().toString() + "\n\n");
            writer.write("Android version: " + Build.VERSION.SDK_INT + '\n');
            writer.write("Device: " + deviceModel + '\n');
            writer.write("App version: " + versionName + " (" + versionCode + ")\n");
            writer.write("App data path: " + filesDir + '\n');
            if (stackTrace != null)
                writer.write("\nStack trace:\n" + stackTrace + '\n');

            String cmd = "echo pref_file--------------------------------------; cat /data/data/com.hijacker/shared_prefs/com.hijacker_preferences.xml;";
            cmd += " echo app directory----------------------------------; " + busybox_tmp + " ls -lR " + filesDir
                    + ';';
            cmd += " echo fw_bcmdhd--------------------------------------; strings /vendor/firmware/fw_bcmdhd.bin | grep \"FWID:\";";
            cmd += " echo ps---------------------------------------------; ps | " + busybox_tmp
                    + " grep -e air -e mdk -e reaver;";
            cmd += " echo busybox----------------------------------------; " + busybox_tmp + ";";
            cmd += " echo logcat-----------------------------------------; logcat -d -v time | " + busybox_tmp
                    + " grep HIJACKER;";
            cmd += " exit\n";
            Log.d("HIJACKER/createReport", cmd);
            shell_in.print(cmd);
            shell_in.flush();

            String buffer = shell_out.readLine();
            while (buffer != null) {
                writer.write(buffer + '\n');
                buffer = shell_out.readLine();
            }

            writer.close();
        } catch (IOException e) {
            if (writer != null) {
                try {
                    writer.close();
                } catch (IOException ignored) {
                }
            }
            return false;
        }
        return true;
    }

    static {
        System.loadLibrary("native-lib");
    }
}