code: package Localization;
  
  import android.graphics.Bitmap;
  import android.graphics.Canvas;
  import android.graphics.Paint;
  import android.graphics.drawable.BitmapDrawable;
  import android.os.AsyncTask;
  import android.util.Log;
  import android.widget.ImageView;
  import android.widget.ProgressBar;
  import android.widget.TextView;
  
  import com.github.chrisbanes.photoview.PhotoViewAttacher;
  
  import java.lang.ref.WeakReference;
  import java.math.BigInteger;
  import java.util.ArrayList;
  import java.util.Collections;
  import java.util.List;
  
  import Localization.structs.SendLocalizationData;
  import Localization.structs.LocalizationResult;
  import security.elgamal.ElGamalCipher;
  import security.elgamal.ElGamalPrivateKey;
  import security.elgamal.ElGamalPublicKey;
  import security.elgamal.ElGamal_Ciphertext;
  import ui.LocalizeActivity;
  import ui.MainActivity;
  
  import security.DGK.DGKOperations;
  import security.DGK.DGKPrivateKey;
  import security.DGK.DGKPublicKey;
  import security.paillier.PaillierCipher;
  import security.paillier.PaillierPrivateKey;
  import security.paillier.PaillierPublicKey;
  
  import static android.graphics.Color.RED;
  import static Localization.LOCALIZATION_SCHEME.*;
  import static ui.LocalizeActivity.off_map;
  
  // https://www.concretepage.com/android/android-asynctask-example-with-progress-bar
  public final class background extends AsyncTask<Void, Integer, Float[]>
  {
      private final static String TAG = "LOCALIZE";
  
      // Packages to send
      private String [] phone_data;
      List<LocalizationResult> fromServer = new ArrayList<>();
      Double [] coordinates = new Double[2];
      private LOCALIZATION_SCHEME LOCALIZATION_SCHEME;
  
      //Keys and read Keys
      private static PaillierPublicKey pk = KeyMaster.pk;
      private static DGKPublicKey DGKpk = KeyMaster.DGKpk;
      private static PaillierPrivateKey sk = KeyMaster.sk;
      private static DGKPrivateKey DGKsk = KeyMaster.DGKsk;
      private static ElGamalPublicKey e_pk = KeyMaster.e_pk;
      private static ElGamalPrivateKey e_sk = KeyMaster.e_sk;
  
      // Localization variables
      private long distanceSUM = 0;
      private double [] w_i = new double[MainActivity.k];
      private boolean isREU2017;
  
      // Time
      private long startTime;
  
      // GUI
      private WeakReference<TextView> results;
      private WeakReference<ProgressBar> progress;
  
      private WeakReference<Bitmap> location;
      private WeakReference<ImageView> imageView;
      private WeakReference<PhotoViewAttacher> my_Attach;
  
      private String [] Found_APs;
      private Integer [] Found_RSS;
  
      // To be sent
      private String [] MAC_send;
      private Integer [] RSS_send;
  
      private Thread t;
  
      // SST REU 2017
      String [] CommonMAC;
  
      public background(int SCHEME, boolean isREU2017, String [] MAC_send, Integer [] RSS_send,
                        TextView output, ProgressBar progress, Bitmap location, ImageView imageView, PhotoViewAttacher my_Attach)
      {
          this.LOCALIZATION_SCHEME = from_int(SCHEME);
          this.isREU2017 = isREU2017;
          this.Found_APs = MAC_send;
          this.Found_RSS = RSS_send;
          this.results = new WeakReference<>(output);
          this.progress = new WeakReference<>(progress);
          this.location = new WeakReference<>(location);
          this.imageView =  new WeakReference<>(imageView);
          this.my_Attach = new WeakReference<>(my_Attach);
          // Fill phone data
          phone_data = MainActivity.getPhoneData();
      }
  
      // Get Columns and normalize them
      protected void onPreExecute()
      {
          startTime = System.nanoTime();
  
          // 1- GET THE COLUMN MAC ADDRESSES
          (t = new Thread(new ClientThread(this))).start();
          try
          {
              t.join();
          }
          catch (InterruptedException e)
          {
              e.printStackTrace();
          }
  
          // 2- ORGANIZE THE AP/RSSI READINGS CORRECTLY!
          MainActivity.VECTOR_SIZE = CommonMAC.length;
          MAC_send = new String [MainActivity.VECTOR_SIZE];
          RSS_send = new Integer[MainActivity.VECTOR_SIZE];
  
          // Initialize...
          for (int i = 0; i < MainActivity.VECTOR_SIZE; i++)
          {
              MAC_send[i] = "NULL";
              RSS_send[i] = -120;
          }
  
          // 2- Prepare data to match with columns in Fingerprint
          for (int column = 0; column < MainActivity.VECTOR_SIZE; column++)
          {
              // Match AP Scan with correct column
              for (int i = 0; i < Found_APs.length; i++)
              {
                  if (Found_APs[i].equals(CommonMAC[column]))
                  {
                      MAC_send[column] = Found_APs[i];
                      RSS_send[column] = Found_RSS[i];
                  }
              }
          }
      }
  
      // Do heavy encryption here
      protected Float [] doInBackground(Void... voids)
      {
          Float [] location = new Float[2];
  
          // S2 comp
          BigInteger[] S2;
  
          // S3
          long S3_plaintext = 0;
          BigInteger S3;
  
          // S3 comp
          BigInteger[] S3_comp;
  
          // For El Gamal
          ElGamal_Ciphertext e_S3;
          List<ElGamal_Ciphertext> e_S2 = new ArrayList<>();
          List<ElGamal_Ciphertext> e_S3_comp = new ArrayList<>();
  
           S2 = new BigInteger[MainActivity.VECTOR_SIZE];
           S3_comp = new BigInteger[MainActivity.VECTOR_SIZE];
  
          publishProgress(2);
  
          switch (LOCALIZATION_SCHEME)
          {
              case PLAIN_MIN:
              case PLAIN_DMA:
              case PLAIN_MCA:
  
                  ClientThread plainSend = new ClientThread(new SendLocalizationData(MAC_send, RSS_send, DGKpk,
                          LOCALIZATION_SCHEME, isREU2017, phone_data, KeyMaster.map_name), this, LOCALIZATION_SCHEME.value);
                  t = new Thread(plainSend);
                  t.start();
                  try
                  {
                      t.join();
                  }
                  catch (InterruptedException e)
                  {
                      e.printStackTrace();
                  }
                  publishProgress(3);
                  Log.d(TAG, "Server Communication completed!");
  
                  // Access data from Send Distance
                  if (isREU2017)
                  {
                      publishProgress(9);
                      // The Server already computed the location!
                      location[0] = coordinates[0].floatValue();
                      location[1] = coordinates[1].floatValue();
                  }
                  else
                  {
                      // get a Send Distance Object from Server...
                      if(LOCALIZATION_SCHEME == PLAIN_DMA)
                      {
                          for(LocalizationResult l: fromServer)
                          {
                              l.plain_decrypt();
                          }
                      }
                      // ---- Sort Distances!-----
                      Collections.sort(fromServer);
                      publishProgress(9);
  
                      if (LOCALIZATION_SCHEME == PLAIN_MIN)
                      {
                          location[0] = fromServer.get(0).getX().floatValue();
                          location[1] = fromServer.get(0).getY().floatValue();
                      }
                      else if (LOCALIZATION_SCHEME == PLAIN_MCA || LOCALIZATION_SCHEME == PLAIN_DMA)
                      {
                          return Phase3();
                      }
                      publishProgress(10);
                  }
                  //Place it on the map...
                  break;
  
              case DGK_MIN:
              case DGK_MCA:
              case DGK_DMA:
                  try
                  {
                      if(LOCALIZATION_SCHEME == DGK_DMA)
                      {
                          for (int i = 0; i < MainActivity.VECTOR_SIZE; i++)
                          {
                              S2[i] = DGKOperations.encrypt(DGKpk, -2 * RSS_send[i]);
                              S3_comp[i] = DGKOperations.encrypt(DGKpk, RSS_send[i] * RSS_send[i]);
                          }
  
                          t = new Thread( new ClientThread(
                                  new SendLocalizationData(MAC_send, S2, null, S3_comp,
                                          DGKpk, LOCALIZATION_SCHEME, isREU2017, phone_data, KeyMaster.map_name),
                                  this, LOCALIZATION_SCHEME.value));
                          t.start();
                      }
                      else
                      {
                          for (int i = 0; i < MainActivity.VECTOR_SIZE; i++)
                          {
                              S2[i] = DGKOperations.encrypt(DGKpk, -2 * RSS_send[i]);
                              S3_plaintext += RSS_send[i] * RSS_send[i];
                          }
  
                          try
                          {
                              S3 = DGKOperations.encrypt(S3_plaintext, DGKpk);
                          }
                          catch(IllegalArgumentException e)
                          {
                              S3 = DGKOperations.encrypt(S3_plaintext % DGKpk.getU().longValue(), DGKpk);
                          }
  
                          t = new Thread( new ClientThread(
                                  new SendLocalizationData(MAC_send, S2, S3, null,
                                          DGKpk, LOCALIZATION_SCHEME, isREU2017, phone_data, KeyMaster.map_name),
                                  this, LOCALIZATION_SCHEME.value));
                          t.start();
                      }
                      t.join();
                      publishProgress(4);
                      Log.d(TAG, "Client Thread starting to submit DGK Data");
  
                  /*
                  I have a sorted distance list
                  And (x, y) and matches are lined up correctly
                  */
  
                      if (isREU2017)
                      {
                          // The Server already computed the location!
                          location[0] = coordinates[0].floatValue();
                          location[1] = coordinates[1].floatValue();
                          publishProgress(10);
                      }
                      else
                      {
                          // 1- Get Data from Server & Decrypt Distances
                          for (int i = 0; i < fromServer.size(); i++)
                          {
                              fromServer.get(i).decrypt_all(DGKsk);
                          }
                          // 3- Sort
                          Collections.sort(fromServer);
                          publishProgress(8);
  
                          // Minimum Distance only
                          if (LOCALIZATION_SCHEME == DGK_MIN)
                          {
                              //Find its matching (x, y)
                              location[0] = fromServer.get(0).getX().floatValue();
                              location[1] = fromServer.get(0).getX().floatValue();
                          }
                          // MCA/DMA
                          if (LOCALIZATION_SCHEME == DGK_MCA || LOCALIZATION_SCHEME == DGK_DMA)
                          {
                              return Phase3();
                          }
                          publishProgress(10);
                      }
                      break;
                  }
                  catch (InterruptedException e)
                  {
                      e.printStackTrace();
                  }
                  catch (IndexOutOfBoundsException | NullPointerException e)
                  {
                      off_map.show();
                  }
  
              case PAILLIER_MIN:
              case PAILLIER_MCA:
              case PAILLIER_DMA:
                  try
                  {
                      if(LOCALIZATION_SCHEME == PAILLIER_DMA)
                      {
                          for (int i = 0; i < MainActivity.VECTOR_SIZE; i++)
                          {
                              S2[i] = PaillierCipher.encrypt(-2 * RSS_send[i], pk);
                              S3_comp[i] = PaillierCipher.encrypt(RSS_send[i] * RSS_send[i], pk);
                          }
  
                          (t = new Thread(new ClientThread(
                                  new SendLocalizationData(MAC_send, S2, null, S3_comp,
                                          pk, DGKpk,
                                          LOCALIZATION_SCHEME, isREU2017, phone_data,  KeyMaster.map_name),
                                  this, LOCALIZATION_SCHEME.value))).start();
                      }
                      else
                      {
                          for (int i = 0; i < MainActivity.VECTOR_SIZE; i++)
                          {
                              S2[i] = PaillierCipher.encrypt(-2 * RSS_send[i], pk);
                              S3_plaintext += RSS_send[i] * RSS_send[i];
                          }
                          S3 = PaillierCipher.encrypt(S3_plaintext, pk);
  
                          (t = new Thread(new ClientThread(
                                  new SendLocalizationData(MAC_send, S2, S3, null,
                                          pk, DGKpk,
                                          LOCALIZATION_SCHEME, isREU2017, phone_data,  KeyMaster.map_name),
                                  this, LOCALIZATION_SCHEME.value))).start();
                      }
                      t.join();
                      publishProgress(4);
  
                      if (isREU2017)
                      {
                          // The Server already computed the location!
                          location[0] = coordinates[0].floatValue();
                          location[1] = coordinates[1].floatValue();
                          publishProgress(10);
                      }
                      else
                      {
                          // 1- Get Data from Server and decrypt distance
                          for (int i = 0; i < fromServer.size(); i++)
                          {
                              fromServer.get(i).decrypt_all(sk);
                          }
                          // 3- Sort data
                          Collections.sort(fromServer);
                          publishProgress(8);
  
                          // Minimum Distance
                          if (LOCALIZATION_SCHEME == PAILLIER_MIN)
                          {
                              // Find its matching (x, y)
                              location[0] = fromServer.get(0).getX().floatValue();
                              location[1] = fromServer.get(0).getY().floatValue();
                          }
                          // MCA/DMA
                          if (LOCALIZATION_SCHEME == PAILLIER_DMA || LOCALIZATION_SCHEME == PAILLIER_MCA)
                          {
                              return Phase3();
                          }
                          publishProgress(10);
                      }
                      break;
                  }
                  catch (InterruptedException e)
                  {
                      e.printStackTrace();
                  }
                  catch (IndexOutOfBoundsException | NullPointerException e)
                  {
                      off_map.show();
                  }
  
              case EL_GAMAL_MIN:
              case EL_GAMAL_MCA:
              case EL_GAMAL_DMA:
                  try
                  {
                      if(LOCALIZATION_SCHEME == EL_GAMAL_DMA)
                      {
                          for (int i = 0; i < MainActivity.VECTOR_SIZE; i++)
                          {
                              e_S2.add(ElGamalCipher.encrypt(e_pk, -2 * RSS_send[i]));
                              e_S3_comp.add(ElGamalCipher.encrypt(e_pk, RSS_send[i] * RSS_send[i]));
                          }
  
                          (t = new Thread(new ClientThread(
                                  new SendLocalizationData(MAC_send, e_S2, null, e_S3_comp,
                                          e_pk, DGKpk,
                                          LOCALIZATION_SCHEME, isREU2017, phone_data,  KeyMaster.map_name),
                                  this, LOCALIZATION_SCHEME.value))).start();
                      }
                      else
                      {
                          for (int i = 0; i < MainActivity.VECTOR_SIZE; i++)
                          {
                              e_S2.add(ElGamalCipher.encrypt(e_pk, -2 * RSS_send[i]));
                              S3_plaintext += RSS_send[i] * RSS_send[i];
                          }
                          e_S3 = ElGamalCipher.encrypt(e_pk, S3_plaintext);
  
                          (t = new Thread(new ClientThread(
                                  new SendLocalizationData(MAC_send, e_S2, e_S3, null,
                                          e_pk, DGKpk,
                                          LOCALIZATION_SCHEME, isREU2017, phone_data, KeyMaster.map_name),
                                  this, LOCALIZATION_SCHEME.value))).start();
                      }
                      t.join();
                      publishProgress(4);
                  /*
                  I have a sorted distance list
                  And (x, y) and matches are lined up correctly
                  */
                      if (isREU2017)
                      {
                          // The Server already computed the location!
                          location[0] = coordinates[0].floatValue();
                          location[1] = coordinates[1].floatValue();
                          publishProgress(10);
                      }
                      else
                      {
                          // 1- Get Data from Server and decrypt Distance
                          for (int i = 0; i < fromServer.size(); i++)
                          {
                              fromServer.get(i).decrypt_all(e_sk);
                          }
                          // 3- Sort data
                          Collections.sort(fromServer);
                          publishProgress(8);
  
                          // Minimum Distance
                          if (LOCALIZATION_SCHEME == EL_GAMAL_MIN)
                          {
                              // Find its matching (x, y)
                              location[0] = fromServer.get(0).getX().floatValue();
                              location[1] = fromServer.get(0).getY().floatValue();
                          }
                          // MCA/DMA
                          if (LOCALIZATION_SCHEME == EL_GAMAL_DMA || LOCALIZATION_SCHEME == EL_GAMAL_MCA)
                          {
                              return Phase3();
                          }
                          publishProgress(10);
                      }
                      break;
                  }
                  catch (IndexOutOfBoundsException | NullPointerException e)
                  {
                      e.printStackTrace();
                      off_map.show();
                  }
                  catch (InterruptedException e)
                  {
                      e.printStackTrace();
                  }
              default:
                  Log.d(TAG, "Case Switch Failure: Localization Scheme set to: " + LOCALIZATION_SCHEME);
                  break;
          }
          return location;
      }
  
      // TO BE USED ONLY BY PLAINTEXT METHODS
      private Float [] Phase3()
      {
          float x = 0;
          float y = 0;
  
          for (int i = 0; i < MainActivity.k; i++)
          {
              distanceSUM += fromServer.get(i).plainDistance;
          }
  
          // Find value of all w_i
          for (int i = 0 ; i < MainActivity.k; i++)
          {
              w_i[i] = ((double) fromServer.get(i).plainDistance/distanceSUM);
              w_i[i] = 1.0 - w_i[i];
              w_i[i] = w_i[i]/(MainActivity.k - 1);
              x += w_i[i] * fromServer.get(i).getX();
              y += w_i[i] * fromServer.get(i).getY();
          }
  
          Float [] final_location = new Float[2];
          final_location[0] = x;
          final_location[1] = y;
          publishProgress(10);
          return final_location;
      }
  
      protected void onProgressUpdate(Integer... values)
      {
          this.progress.get().setProgress(values[0]);
      }
  
      // Do the Drawing!
      protected void onPostExecute(Float[] result)
      {
          BitmapDrawable drawTrain;
          final Bitmap mutableBitmap;
          Canvas drawFlags;
          int flagH;
          int flagW;
  
          drawTrain = (BitmapDrawable) imageView.get().getDrawable();
          mutableBitmap = drawTrain.getBitmap().copy(Bitmap.Config.ARGB_8888, true);
          drawFlags = new Canvas(mutableBitmap);
  
          Log.d(TAG, "Draw X bit: " + result[0] + ", Draw Y bit: " + result[1]);
  
          //Dimensions of Flag
          flagW = location.get().getWidth();
          flagH = location.get().getHeight();
  
          try
          {
              drawFlags.drawBitmap(location.get(), result[0] - (float) (flagW / 2), result[1] - (float) (flagH / 2), new Paint(RED));
              imageView.get().post(new Runnable() {
                  public void run() {
                      imageView.get().setImageBitmap(mutableBitmap);
                  }
              });
              my_Attach.get().update();
          }
          catch (NullPointerException e)
          {
              // This will happen if your FSF is too strict or you are off the map.
              off_map.show();
          }
          finally
          {
              String msg = (System.nanoTime() - startTime) / MainActivity.BILLION + " seconds.";
              results.get().setText(msg);
          }
      }
  }
  