com.secupwn.aimsicd.utils.Helpers.java Source code

Java tutorial

Introduction

Here is the source code for com.secupwn.aimsicd.utils.Helpers.java

Source

/**
 *     Copyright (C) 2013  Louis Teboul    <louisteboul@gmail.com>
 *
 *     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 2 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, write to the Free Software Foundation, Inc.,
 *     51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
 **/

package com.secupwn.aimsicd.utils;

import android.app.AlertDialog;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.net.ConnectivityManager;
import android.net.NetworkInfo;
import android.support.v4.app.Fragment;
import android.text.TextUtils;

import com.secupwn.aimsicd.R;
import com.secupwn.aimsicd.ui.fragments.MapFragment;
import com.secupwn.aimsicd.constants.DrawerMenu;
import com.secupwn.aimsicd.service.AimsicdService;
import com.secupwn.aimsicd.service.CellTracker;

import java.io.BufferedReader;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;

import io.freefair.android.injection.app.InjectionAppCompatActivity;
import io.freefair.android.util.logging.AndroidLogger;
import io.freefair.android.util.logging.Logger;

/**
 *
 * Description:     This class contain many various functions to:
 *
 *                  - present Toast messages
 *                  - getTimestamp
 *                  - Check network connectivity
 *                  - Download CSV file with BTS data via HTTP API from OCID servers
 *                  - Convert ByteToString
 *                  - unpackListOfStrings
 *                  - Check if SD is writable
 *                  - get System properties
 *                  - Check for SU and BusyBox
 *
 */
public class Helpers {

    private static final Logger log = AndroidLogger.forClass(Helpers.class);
    private static final int CHARS_PER_LINE = 34;

    /**
     * Description:      Long toast message
     *
     * Notes:
     *
     *       This is only a proxy method to the Toaster class.
     *       It also takes care of using the Toaster's Singleton.
     *
     * @param context Application Context
     * @param msg     Message to send
     */
    public static void msgLong(Context context, String msg) {
        Toaster.msgLong(context, msg);
    }

    /**
     * Description:      Short toast message
     *
     * Notes:
     *
     *       This is only a proxy method to the Toaster class.
     *       It also takes care of using the Toaster's Singleton.
     *
     * @param context Application Context
     * @param msg     Message to send
     */
    public static void msgShort(Context context, String msg) {
        Toaster.msgShort(context, msg);
    }

    /**
     * Description:      Long toast message
     *
     * Notes:
     *
     *       This is only a proxy method to the Toaster class.
     *       It also takes care of using the Toaster's Singleton.
     *
     * @param context Application Context
     * @param msg     Message to send
     */
    public static void sendMsg(Context context, String msg) {
        Toaster.msgLong(context, msg);
    }

    /**
     * Checks if Network connectivity is available to download OpenCellID data
     * Requires:        android:name="android.permission.ACCESS_NETWORK_STATE"
     */
    public static Boolean isNetAvailable(Context context) {
        try {
            ConnectivityManager cM = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
            NetworkInfo wifiInfo = cM.getNetworkInfo(ConnectivityManager.TYPE_WIFI);
            NetworkInfo mobileInfo = cM.getNetworkInfo(ConnectivityManager.TYPE_MOBILE);
            if (wifiInfo != null && mobileInfo != null) {
                return wifiInfo.isConnected() || mobileInfo.isConnected();
            }
        } catch (Exception e) {
            log.error(e.getMessage(), e);
        }
        return false;
    }

    /**
     * Description:      Requests Cell data from OpenCellID.org (OCID).
     *
     * Notes:
     *
     *       The free OCID API has a download limit of 1000 BTSs for each download.
     *       Thus we need to carefully select the area we choose to download and make sure it is
     *       centered on the current GPS location. (It is also possible to query the number of
     *       cells in a particular bounding box (bbox), and use that.)
     *
     *       The bbox is described in the OCID API here:
     *       http://wiki.opencellid.org/wiki/API#Getting_the_list_of_cells_in_a_specified_area
     *
     *       In an urban area, we could try to limit ourselves to an area radius of ~2 Km.
     *       The (GSM) Timing Advance is limiting us to 35 Km.
     *
     *       The OCID API payload:
     *
     *       required:   key=<apiKey>&BBOX=<latmin>,<lonmin>,<latmax>,<lonmax>
     *       optional:   &mcc=<mcc>&mnc=<mnc>&lac=<lac>&radio=<radio>
     *                   &limit=<limit>&offset=<offset>&format=<format>
     *
     *       Our API query is using:  (Lat1,Lon1, Lat2,Lon2, mcc,mnc,lac)
     *
     *  Issues:
     *
     *      [ ] A too restrictive payload leads to many missing BTS in area, but a too liberal
     *          payload would return many less relevant ones and would cause us to reach the
     *          OCID API 1000 BTS download limit much faster. The solution would be to make the
     *          BBOX smaller, but that in turn, would result in the loss of some more distant,
     *          but still available towers. Possibly making them appears as RED, even when they
     *          are neither fake nor IMSI-catchers. However, a more realistic BTS picture is
     *          more useful, especially when sharing that info across different devices using
     *          on different RAT and MNO.
     *
     *      [ ] We need a smarter way to handle the downloading of the BTS data. The OCID API
     *          allows for finding how many cells are contained in a query. We can the use this
     *          info to loop the max query size to get all those cells. The Query format is:
     *
     *          GET:        http://<WebServiceURL>/cell/getInAreaSize
     *
     *          The OCID API payload:
     *
     *          required:     key=<apiKey>&BBOX=<latmin>,<lonmin>,<latmax>,<lonmax>
     *          optional:     &mcc=<mcc>&mnc=<mnc>&lac=<lac>&radio=<radio>&format=<format>
     *
     *          result:       JSON:
     *                              {
     *                                count: 123
     *                              }
     *
     *      [x]  Q:  How is the BBOX actually calculated from the "radius"?
     *           A:  It's calculated as an inscribed circle to a square of 2*R on each side.
     *               See ./utils/GeoLocation.java
     *
     *  Dependencies:    GeoLocation.java
     *
     *  Used:
     * @param cell Current Cell Information
     *
     */
    public static void getOpenCellData(InjectionAppCompatActivity injectionActivity, Cell cell, char type) {
        getOpenCellData(injectionActivity, cell, type, null);
    }

    public static void getOpenCellData(InjectionAppCompatActivity injectionActivity, Cell cell, char type,
            final AimsicdService service) {
        if (Helpers.isNetAvailable(injectionActivity)) {
            if (!"NA".equals(CellTracker.OCID_API_KEY)) {
                double earthRadius = 6371.01; // [Km]
                int radius = 2; // Use a 2 Km radius with center at GPS location.

                if (Double.doubleToRawLongBits(cell.getLat()) != 0
                        && Double.doubleToRawLongBits(cell.getLon()) != 0) {
                    //New GeoLocation object to find bounding Coordinates
                    GeoLocation currentLoc = GeoLocation.fromDegrees(cell.getLat(), cell.getLon());

                    //Calculate the Bounding Box Coordinates using an N Km "radius" //0=min, 1=max
                    GeoLocation[] boundingCoords = currentLoc.boundingCoordinates(radius, earthRadius);
                    String boundParameter;

                    //Request OpenCellID data for Bounding Coordinates (0 = min, 1 = max)
                    boundParameter = String.valueOf(boundingCoords[0].getLatitudeInDegrees()) + ","
                            + String.valueOf(boundingCoords[0].getLongitudeInDegrees()) + ","
                            + String.valueOf(boundingCoords[1].getLatitudeInDegrees()) + ","
                            + String.valueOf(boundingCoords[1].getLongitudeInDegrees());

                    log.info("OCID BBOX is set to: " + boundParameter + "  with radius " + radius + " Km.");

                    StringBuilder sb = new StringBuilder();
                    sb.append("http://www.opencellid.org/cell/getInArea?key=").append(CellTracker.OCID_API_KEY)
                            .append("&BBOX=").append(boundParameter);

                    log.info("OCID MCC is set to: " + cell.getMobileCountryCode());
                    if (cell.getMobileCountryCode() != Integer.MAX_VALUE) {
                        sb.append("&mcc=").append(cell.getMobileCountryCode());
                    }
                    log.info("OCID MNC is set to: " + cell.getMobileNetworkCode());
                    if (cell.getMobileNetworkCode() != Integer.MAX_VALUE) {
                        sb.append("&mnc=").append(cell.getMobileNetworkCode());
                    }

                    sb.append("&format=csv");
                    new RequestTask(injectionActivity, type, new RequestTask.AsyncTaskCompleteListener() {
                        @Override
                        public void onAsyncTaskSucceeded() {
                            log.verbose(
                                    "RequestTask's OCID download was successful. Callback rechecking connected cell against database");
                            service.getCellTracker().compareLacAndOpenDb();
                        }

                        @Override
                        public void onAsyncTaskFailed(String result) {
                        }
                    }).execute(sb.toString());
                }
            } else {
                Fragment myFragment = injectionActivity.getSupportFragmentManager()
                        .findFragmentByTag(String.valueOf(DrawerMenu.ID.MAIN.ALL_CURRENT_CELL_DETAILS));
                if (myFragment instanceof MapFragment) {
                    ((MapFragment) myFragment).setRefreshActionButtonState(false);
                }
                Helpers.sendMsg(injectionActivity,
                        injectionActivity.getString(R.string.no_opencellid_key_detected));
            }
        } else {
            Fragment myFragment = injectionActivity.getSupportFragmentManager()
                    .findFragmentByTag(String.valueOf(DrawerMenu.ID.MAIN.ALL_CURRENT_CELL_DETAILS));
            if (myFragment instanceof MapFragment) {
                ((MapFragment) myFragment).setRefreshActionButtonState(false);
            }

            final AlertDialog.Builder builder = new AlertDialog.Builder(injectionActivity);
            builder.setTitle(R.string.no_network_connection_title)
                    .setMessage(R.string.no_network_connection_message);
            builder.create().show();
        }
    }

    /**
     * Return a String List representing response from invokeOemRilRequestRaw
     *
     * @param aob Byte array response from invokeOemRilRequestRaw
     */
    public static List<String> unpackByteListOfStrings(byte aob[]) {

        if (aob.length == 0) {
            // WARNING: This one is very chatty!
            log.verbose("invokeOemRilRequestRaw: byte-list response Length = 0");
            return Collections.emptyList();
        }
        int lines = aob.length / CHARS_PER_LINE;
        String[] display = new String[lines];

        for (int i = 0; i < lines; i++) {
            int offset, byteCount;
            offset = i * CHARS_PER_LINE + 2;
            byteCount = 0;

            if (offset + byteCount >= aob.length) {
                log.error("Unexpected EOF");
                break;
            }
            while (aob[offset + byteCount] != 0 && (byteCount < CHARS_PER_LINE)) {
                byteCount += 1;
                if (offset + byteCount >= aob.length) {
                    log.error("Unexpected EOF");
                    break;
                }
            }
            display[i] = new String(aob, offset, byteCount).trim();
        }
        int newLength = display.length;
        while (newLength > 0 && TextUtils.isEmpty(display[newLength - 1])) {
            newLength -= 1;
        }
        return Arrays.asList(Arrays.copyOf(display, newLength));
    }

    public static String getSystemProp(Context context, String prop, String def) {
        String result = null;
        try {
            result = SystemPropertiesReflection.get(context, prop);
        } catch (IllegalArgumentException iae) {
            log.error("Failed to get system property: " + prop, iae);
        }
        return result == null ? def : result;
    }

    public static String convertStreamToString(InputStream is) throws Exception {
        BufferedReader reader = new BufferedReader(new InputStreamReader(is));
        StringBuilder sb = new StringBuilder();
        String line;
        while ((line = reader.readLine()) != null) {
            sb.append(line).append("\n");
        }
        reader.close();
        return sb.toString();
    }

    /**
     * Description:     Deletes the entire database by removing internal SQLite DB file
     *
        
     *
     * Dependencies:    Used in AIMSICD.java
     *
     * Notes:           See Android developer info: http://tinyurl.com/psz8vmt
     *
     *              WARNING!
     *              This deletes the entire database, thus any subsequent DB access will FC app.
     *              Therefore we need to either restart app or run AIMSICDDbAdapter, to rebuild DB.
     *              See: #581
     *
     *              In addition, since SQLite is kept in memory during lifetime of App, and
     *              is using Journaling, we have to restart app in order to clear old data
     *              already in memory.
     *
     * @param pContext Context of Activity
     */
    public static void askAndDeleteDb(final Context pContext) {
        AlertDialog lAlertDialog = new AlertDialog.Builder(pContext)
                .setNegativeButton(R.string.open_cell_id_button_cancel, new DialogInterface.OnClickListener() {
                    @Override
                    public void onClick(DialogInterface dialog, int which) {
                    }
                }).setPositiveButton(R.string.open_cell_id_button_ok, new DialogInterface.OnClickListener() {
                    @Override
                    public void onClick(DialogInterface dialog, int which) {
                        // Probably put in try/catch in case file removal fails...
                        pContext.stopService(new Intent(pContext, AimsicdService.class));
                        pContext.deleteDatabase("aimsicd.db");
                        new RealmHelper(pContext);
                        pContext.startService(new Intent(pContext, AimsicdService.class));
                        msgLong(pContext, pContext.getString(R.string.delete_database_msg_success));
                    }
                }).setMessage(pContext.getString(R.string.clear_database_question))
                .setTitle(R.string.clear_database).setCancelable(false)
                .setIcon(R.drawable.ic_action_delete_database).create();
        lAlertDialog.show();
    }
}