org.thaliproject.nativetest.app.ConnectionEngine.java Source code

Java tutorial

Introduction

Here is the source code for org.thaliproject.nativetest.app.ConnectionEngine.java

Source

/* Copyright (c) 2016 Microsoft Corporation. This software is licensed under the MIT License.
 * See the license file delivered with this project for further information.
 */
package org.thaliproject.nativetest.app;

import android.app.Activity;
import android.app.AlertDialog;
import android.bluetooth.BluetoothSocket;
import android.content.Context;
import android.content.DialogInterface;
import android.content.pm.PackageManager;
import android.os.Build;
import android.os.CountDownTimer;
import android.support.v4.app.ActivityCompat;
import android.support.v4.content.ContextCompat;
import android.util.Log;
import org.thaliproject.nativetest.app.fragments.LogFragment;
import org.thaliproject.nativetest.app.model.Connection;
import org.thaliproject.nativetest.app.model.PeerAndConnectionModel;
import org.thaliproject.nativetest.app.model.Settings;
import org.thaliproject.p2p.btconnectorlib.ConnectionManager;
import org.thaliproject.p2p.btconnectorlib.DiscoveryManager;
import org.thaliproject.p2p.btconnectorlib.DiscoveryManagerSettings;
import org.thaliproject.p2p.btconnectorlib.PeerProperties;
import org.thaliproject.p2p.btconnectorlib.utils.BluetoothSocketIoThread;
import java.io.IOException;
import java.util.UUID;

/**
 * This class is responsible for managing both peer discovery and connections.
 */
public class ConnectionEngine implements ConnectionManager.ConnectionManagerListener,
        DiscoveryManager.DiscoveryManagerListener, Connection.Listener {
    protected static final String TAG = ConnectionEngine.class.getName();

    // Service type and UUID has to be application/service specific.
    // The app will only connect to peers with the matching values.
    public static final String PEER_NAME = Build.MANUFACTURER + "_" + Build.MODEL; // Use manufacturer and device model name as the peer name
    protected static final String SERVICE_TYPE = "ThaliTestSampleApp._tcp";
    protected static final String SERVICE_UUID_AS_STRING = "9ab3c173-66d5-4da6-9e23-e8ce520b479b";
    protected static final String SERVICE_NAME = "Thali Test Sample App";
    protected static final UUID SERVICE_UUID = UUID.fromString(SERVICE_UUID_AS_STRING);
    protected static final long CHECK_CONNECTIONS_INTERVAL_IN_MILLISECONDS = 10000;
    protected static final long NOTIFY_STATE_CHANGED_DELAY_IN_MILLISECONDS = 500;
    protected static int DURATION_OF_DEVICE_DISCOVERABLE_IN_SECONDS = 60;
    private static final int PERMISSION_REQUEST_ACCESS_COARSE_LOCATION = 1;

    protected Context mContext = null;
    protected Activity mActivity = null;
    protected Settings mSettings = null;
    protected ConnectionManager mConnectionManager = null;
    protected DiscoveryManager mDiscoveryManager = null;
    protected PeerAndConnectionModel mModel = null;
    protected CountDownTimer mCheckConnectionsTimer = null;
    protected CountDownTimer mNotifyStateChangedTimer = null;
    private AlertDialog mAlertDialog = null;
    private boolean mIsShuttingDown = false;

    /**
     * Constructor.
     */
    public ConnectionEngine(Context context, Activity activity) {
        mContext = context;
        mActivity = activity;
        mModel = PeerAndConnectionModel.getInstance();

        mConnectionManager = new ConnectionManager(mContext, this, SERVICE_UUID, SERVICE_NAME);
        mConnectionManager.setPeerName(PEER_NAME);

        mDiscoveryManager = new DiscoveryManager(mContext, this, SERVICE_UUID, SERVICE_TYPE);
        mDiscoveryManager.setPeerName(PEER_NAME);
    }

    /**
     * Loads the settings and binds the discovery manager to the settings instance.
     */
    public void bindSettings() {
        mSettings = Settings.getInstance(mContext);
        mSettings.setConnectionManager(mConnectionManager);
        mSettings.setDiscoveryManager(mDiscoveryManager);
        mSettings.load();
    }

    /**
     * Starts both the connection and the discovery manager.
     *
     * @return True, if started successfully. False otherwise.
     */
    public synchronized boolean start() {
        mIsShuttingDown = false;

        boolean shouldConnectionManagerBeRunning = mSettings.getListenForIncomingConnections();
        boolean wasConnectionManagerStarted = false;

        if (shouldConnectionManagerBeRunning) {
            wasConnectionManagerStarted = mConnectionManager.startListeningForIncomingConnections();

            if (!wasConnectionManagerStarted) {
                Log.e(TAG, "start: Failed to start the connection manager");
                LogFragment.logError("Failed to start the connection manager");
            }
        }

        boolean shouldDiscoveryManagerBeRunning = (mSettings.getEnableBleDiscovery()
                || mSettings.getEnableWifiDiscovery());
        boolean wasDiscoveryManagerStarted = false;

        if (shouldDiscoveryManagerBeRunning) {
            wasDiscoveryManagerStarted = (mDiscoveryManager
                    .getState() != DiscoveryManager.DiscoveryManagerState.NOT_STARTED
                    || mDiscoveryManager.start(true, true));

            if (!shouldDiscoveryManagerBeRunning) {
                Log.e(TAG, "start: Failed to start the discovery manager");
                LogFragment.logError("Failed to start the discovery manager");
            }
        }

        if (mCheckConnectionsTimer != null) {
            mCheckConnectionsTimer.cancel();
            mCheckConnectionsTimer = null;
        }

        mCheckConnectionsTimer = new CountDownTimer(CHECK_CONNECTIONS_INTERVAL_IN_MILLISECONDS,
                CHECK_CONNECTIONS_INTERVAL_IN_MILLISECONDS) {
            @Override
            public void onTick(long l) {
                // Not used
            }

            @Override
            public void onFinish() {
                sendPingToAllPeers();
                mCheckConnectionsTimer.start();
            }
        };

        return ((!shouldConnectionManagerBeRunning || wasConnectionManagerStarted)
                && (!shouldDiscoveryManagerBeRunning || wasDiscoveryManagerStarted));
    }

    /**
     * Stops both the connection and the discovery manager.
     */
    public synchronized void stop() {
        mIsShuttingDown = true;

        if (mCheckConnectionsTimer != null) {
            mCheckConnectionsTimer.cancel();
        }

        mConnectionManager.stopListeningForIncomingConnections();
        mConnectionManager.cancelAllConnectionAttempts();
        mDiscoveryManager.stop();

        mModel.closeAllConnections();
        mModel.clearPeers();
    }

    /**
     * Disposes both the discovery and the connection manager.
     * After calling this method, this instance of the connection engine cannot be used again.
     */
    public void dispose() {
        stop();
        mDiscoveryManager.dispose();
        mConnectionManager.dispose();
        mDiscoveryManager = null;
        mConnectionManager = null;
    }

    /**
     * Connects to the peer with the given properties.
     *
     * @param peerProperties The properties of the peer to connect to.
     */
    public synchronized void connect(PeerProperties peerProperties) {
        if (peerProperties != null) {
            if (mConnectionManager.connect(peerProperties)) {
                LogFragment.logMessage("Trying to connect to peer " + peerProperties.toString());
                mModel.addPeerBeingConnectedTo(peerProperties);
                MainActivity.updateOptionsMenu();
            } else {
                String errorMessageStub = "Failed to start connecting to peer ";
                Log.e(TAG, "connect: " + errorMessageStub + peerProperties.toString());
                LogFragment.logError(errorMessageStub + peerProperties.toString());
                MainActivity.showToast(errorMessageStub + peerProperties.getName());
            }
        }
    }

    /**
     * Starts the Bluetooth device discovery.
     */
    public void startBluetoothDeviceDiscovery() {
        mDiscoveryManager.getBluetoothMacAddressResolutionHelper().startBluetoothDeviceDiscovery();
    }

    /**
     * Makes the device discoverable.
     */
    public void makeDeviceDiscoverable() {
        mDiscoveryManager.makeDeviceDiscoverable(DURATION_OF_DEVICE_DISCOVERABLE_IN_SECONDS);
    }

    /**
     * Starts sending data to the peer with the given properties.
     *
     * @param peerProperties The properties of the peer to send data to.
     */
    public synchronized void startSendingData(PeerProperties peerProperties) {
        Connection connection = mModel.getConnectionToPeer(peerProperties, false);

        if (connection == null) {
            connection = mModel.getConnectionToPeer(peerProperties, true);
        }

        if (connection != null) {
            connection.sendData();
            LogFragment.logMessage(
                    "Sending " + String.format("%.2f", connection.getTotalDataAmountCurrentlySendingInMegaBytes())
                            + " MB to peer " + peerProperties.toString());
            mModel.notifyListenersOnDataChanged(); // To update the progress bar
            MainActivity.updateOptionsMenu();
        } else {
            Log.e(TAG, "startSendingData: No connection found");
        }
    }

    /**
     * Called when the user grants/denies a permission request.
     *
     * @param requestCode The request code associated with the permission request.
     * @param permissions The permissions in question.
     * @param grantResults The grant results (granted/denied).
     */
    public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {
        Log.d(TAG, "onRequestPermissionsResult: Request code: " + requestCode);

        for (int i = 0; (i < permissions.length && i < grantResults.length); ++i) {
            Log.d(TAG, "onRequestPermissionsResult: Permission: " + permissions[i] + ", grant result: "
                    + grantResults[i]);
        }

        if (requestCode == PERMISSION_REQUEST_ACCESS_COARSE_LOCATION && grantResults.length > 0) {
            if (grantResults[0] == PackageManager.PERMISSION_GRANTED) {
                Log.d(TAG, "onRequestPermissionsResult: Permission granted");
                mDiscoveryManager.start(true, true);
            } else {
                Log.e(TAG, "onRequestPermissionsResult: Permission denied");
            }

            if (mAlertDialog != null) {
                mAlertDialog.dismiss();
                mAlertDialog = null;
            }
        }
    }

    @Override
    public void onConnectionManagerStateChanged(ConnectionManager.ConnectionManagerState connectionManagerState) {
        Log.v(TAG, "onConnectionManagerStateChanged: " + connectionManagerState);
        restartNotifyStateChangedTimer();
    }

    /**
     * Constructs a Bluetooth socket IO thread for the new connection and adds it to the list of
     * connections.
     *
     * @param bluetoothSocket The Bluetooth socket.
     * @param isIncoming If true, this is an incoming connection. If false, this is an outgoing connection.
     * @param peerProperties The peer properties.
     */
    @Override
    public void onConnected(BluetoothSocket bluetoothSocket, boolean isIncoming, PeerProperties peerProperties) {
        Log.i(TAG, "onConnected: " + (isIncoming ? "Incoming" : "Outgoing") + " connection: "
                + peerProperties.toString());
        mModel.removePeerBeingConnectedTo(peerProperties);
        Connection connection = null;

        try {
            connection = new Connection(this, bluetoothSocket, peerProperties, isIncoming);
        } catch (Exception e) {
            Log.e(TAG, "onConnected: Failed to create a socket IO thread instance: " + e.getMessage(), e);

            try {
                bluetoothSocket.close();
            } catch (IOException e2) {
            }
        }

        if (connection != null) {
            final String peerName = connection.getPeerProperties().getName();
            final boolean wasIncoming = connection.getIsIncoming();

            mModel.addOrRemoveConnection(connection, true);

            MainActivity.showToast(peerName + " connected (is " + (wasIncoming ? "incoming" : "outgoing") + ")");

            if (isIncoming) {
                // Add peer, if it was not discovered before
                mModel.addOrUpdatePeer(peerProperties);
                mDiscoveryManager.getPeerModel().addOrUpdateDiscoveredPeer(peerProperties);
            }

            // Update the peer name, if already in the model
            mModel.updatePeerName(peerProperties);

            LogFragment.logMessage((isIncoming ? "Incoming" : "Outgoing") + " connection established to peer "
                    + peerProperties.toString());
        }

        final int totalNumberOfConnections = mModel.getTotalNumberOfConnections();

        Log.i(TAG, "onConnected: Total number of connections is now " + totalNumberOfConnections);

        if (totalNumberOfConnections == 1) {
            mCheckConnectionsTimer.cancel();
            mCheckConnectionsTimer.start();
        }

        MainActivity.updateOptionsMenu();
    }

    @Override
    public void onConnectionTimeout(PeerProperties peerProperties) {
        Log.i(TAG, "onConnectionTimeout: " + peerProperties);

        if (peerProperties != null) {
            mModel.removePeerBeingConnectedTo(peerProperties);

            MainActivity.showToast("Failed to connect to " + peerProperties.getName() + ": Connection timeout");
            LogFragment.logError("Failed to connect to peer " + peerProperties.toString() + ": Connection timeout");
        } else {
            MainActivity.showToast("Failed to connect: Connection timeout");
            LogFragment.logError("Failed to connect: Connection timeout");
        }

        MainActivity.updateOptionsMenu();
    }

    @Override
    public void onConnectionFailed(PeerProperties peerProperties, String errorMessage) {
        Log.i(TAG, "onConnectionFailed: " + errorMessage + ": " + peerProperties);

        if (peerProperties != null) {
            mModel.removePeerBeingConnectedTo(peerProperties);

            MainActivity.showToast("Failed to connect to " + peerProperties.getName()
                    + ((errorMessage != null) ? (": " + errorMessage) : ""));
            LogFragment.logError("Failed to connect to peer " + peerProperties.toString()
                    + ((errorMessage != null) ? (": " + errorMessage) : ""));

        } else {
            MainActivity.showToast("Failed to connect" + ((errorMessage != null) ? (": " + errorMessage) : ""));
            LogFragment.logError("Failed to connect" + ((errorMessage != null) ? (": " + errorMessage) : ""));
        }

        MainActivity.updateOptionsMenu();
    }

    @Override
    public boolean onPermissionCheckRequired(String permission) {
        int permissionCheck = PackageManager.PERMISSION_DENIED;

        if (mActivity != null) {
            permissionCheck = ContextCompat.checkSelfPermission(mActivity, permission);
            Log.i(TAG, "onPermissionCheckRequired: " + permission + ": " + permissionCheck);

            if (permissionCheck == PackageManager.PERMISSION_DENIED) {
                requestPermission(permission);
            }
        } else {
            Log.e(TAG, "onPermissionCheckRequired: The activity is null");
        }

        return (permissionCheck == PackageManager.PERMISSION_GRANTED);
    }

    /**
     * @param isEnabled True, if enabled. False, if disabled.
     */
    @Override
    public void onWifiEnabledChanged(boolean isEnabled) {
        Log.d(TAG, "onWifiEnabledChanged: " + isEnabled);
    }

    /**
     * @param isEnabled True, if enabled. False, if disabled.
     */
    @Override
    public void onBluetoothEnabledChanged(boolean isEnabled) {
        Log.d(TAG, "onBluetoothEnabledChanged: " + isEnabled);
    }

    /**
     * @param state The new state.
     * @param isDiscovering True, if peer discovery is active. False otherwise.
     * @param isAdvertising True, if advertising is active. False otherwise.
     */
    @Override
    public void onDiscoveryManagerStateChanged(DiscoveryManager.DiscoveryManagerState state, boolean isDiscovering,
            boolean isAdvertising) {
        Log.v(TAG, "onDiscoveryManagerStateChanged: " + state + ", " + isDiscovering + ", " + isAdvertising);
        restartNotifyStateChangedTimer();
    }

    @Override
    public void onProvideBluetoothMacAddressRequest(String requestId) {
        // TODO
    }

    @Override
    public void onPeerReadyToProvideBluetoothMacAddress() {
        if (!DiscoveryManagerSettings.getInstance(null).getAutomateBluetoothMacAddressResolution()) {
            mDiscoveryManager.makeDeviceDiscoverable(DURATION_OF_DEVICE_DISCOVERABLE_IN_SECONDS);
        }
    }

    @Override
    public void onBluetoothMacAddressResolved(String bluetoothMacAddress) {
        Log.i(TAG, "onBluetoothMacAddressResolved: " + bluetoothMacAddress);
        LogFragment.logMessage("Bluetooth MAC address resolved: " + bluetoothMacAddress);
        start();
    }

    @Override
    public void onPeerDiscovered(PeerProperties peerProperties) {
        Log.i(TAG, "onPeerDiscovered: " + peerProperties.toString());

        if (mModel.addOrUpdatePeer(peerProperties)) {
            LogFragment.logMessage("Peer " + peerProperties.toString() + " discovered");
            autoConnectIfEnabled(peerProperties);
        }
    }

    @Override
    public void onPeerUpdated(PeerProperties peerProperties) {
        Log.i(TAG, "onPeerUpdated: " + peerProperties.toString());
        mModel.addOrUpdatePeer(peerProperties);
        LogFragment.logMessage("Peer " + peerProperties.toString() + " updated");
    }

    @Override
    public void onPeerLost(PeerProperties peerProperties) {
        Log.i(TAG, "onPeerLost: " + peerProperties.toString());

        if (mModel.hasConnectionToPeer(peerProperties)) {
            // We are connected so it can't be lost
            mDiscoveryManager.getPeerModel().addOrUpdateDiscoveredPeer(peerProperties);
        } else {
            mModel.removePeer(peerProperties);
            LogFragment.logMessage("Peer " + peerProperties.toString() + " lost");
        }
    }

    @Override
    public void onBytesRead(byte[] bytes, int numberOfBytesRead, BluetoothSocketIoThread bluetoothSocketIoThread) {
        Log.v(TAG,
                "onBytesRead: Received " + numberOfBytesRead + " bytes from peer "
                        + (bluetoothSocketIoThread.getPeerProperties() != null
                                ? bluetoothSocketIoThread.getPeerProperties().toString()
                                : "<no ID>"));
    }

    @Override
    public void onBytesWritten(byte[] bytes, int numberOfBytesWritten,
            BluetoothSocketIoThread bluetoothSocketIoThread) {
        Log.v(TAG,
                "onBytesWritten: Sent " + numberOfBytesWritten + " bytes to peer "
                        + (bluetoothSocketIoThread.getPeerProperties() != null
                                ? bluetoothSocketIoThread.getPeerProperties().toString()
                                : "<no ID>"));
    }

    @Override
    public void onDisconnected(String reason, final Connection connection) {
        Log.i(TAG,
                "onDisconnected: Peer " + connection.getPeerProperties().toString() + " disconnected: " + reason);
        final PeerProperties peerProperties = connection.getPeerProperties();
        final String peerName = peerProperties.getName();
        final boolean wasIncoming = connection.getIsIncoming();

        synchronized (this) {
            new Thread() {
                @Override
                public void run() {
                    if (!mModel.addOrRemoveConnection(connection, false) && !mIsShuttingDown) {
                        Log.e(TAG,
                                "onDisconnected: Failed to remove the connection, because not found in the list");
                    } else if (!mIsShuttingDown) {
                        Log.d(TAG,
                                "onDisconnected: Connection " + connection.toString() + " removed from the list");
                    }

                    connection.close(true);

                    final int totalNumberOfConnections = mModel.getTotalNumberOfConnections();

                    Log.i(TAG, "onDisconnected: Total number of connections is now " + totalNumberOfConnections);

                    if (totalNumberOfConnections == 0) {
                        mCheckConnectionsTimer.cancel();
                    }

                    // ToDo: we are inside the run() of a new Thread - that the autoconnect will attempt to create a handler - when looper.prepare() was never called.
                    // ToDo: for now, disable this automatic reconnect that seems to be the logic.  Let the Engine decide which peer to intiate and not just autoconnnect to the broken peer.
                    // Reverse the transfer with that partner, doing back and forth?
                    if (!mIsShuttingDown) {
                        Log.i(TAG, "onDisconnected: initiating reverse direction transfer to peer");
                        try {
                            boolean goodConnect = autoConnectIfEnabled(peerProperties);
                            Log.i(TAG, "onDisconnected: did reverse direction transfer to peer, result: "
                                    + goodConnect);
                        } catch (Exception e0) {
                            BridgeSpot.exceptionProblemCount++;
                            Log.e(TAG, "Exception in the reverse direction transfer", e0);
                        }
                    }

                    MainActivity.updateOptionsMenu();
                }
            }.start();
        }

        MainActivity.showToast(peerName + " disconnected (was " + (wasIncoming ? "incoming" : "outgoing") + ")");
        LogFragment.logMessage("Peer " + peerProperties.toString() + " disconnected (was "
                + (wasIncoming ? "incoming" : "outgoing") + ")");
    }

    @Override
    public void onSendDataProgress(float progressInPercentages, float transferSpeed, PeerProperties receivingPeer) {
        Log.d(TAG,
                "onSendDataProgress: " + Math.round(progressInPercentages * 100) + " % " + transferSpeed + " MB/s");
        mModel.notifyListenersOnDataChanged(); // To update the progress bar
    }

    @Override
    public void onDataSent(float dataSentInMegaBytes, float transferSpeed, PeerProperties receivingPeer) {
        String message = "Sent " + String.format("%.2f", dataSentInMegaBytes) + " MB with transfer speed of "
                + String.format("%.3f", transferSpeed) + " MB/s";

        Log.i(TAG, "onDataSent: " + message + " to peer " + receivingPeer);
        LogFragment.logMessage(message + " to peer " + receivingPeer);
        MainActivity.showToast(message + " to peer " + receivingPeer.getName());
        mModel.notifyListenersOnDataChanged(); // To update the progress bar
        MainActivity.updateOptionsMenu();
    }

    /**
     * Sends a ping message to all connected peers.
     */
    protected synchronized void sendPingToAllPeers() {
        for (Connection connection : mModel.getConnections()) {
            connection.ping();
        }
    }

    /**
     * Tries to connect to the peer with the given properties if the auto-connect is enabled.
     *
     * @param peerProperties The peer properties.
     */
    protected synchronized boolean autoConnectIfEnabled(PeerProperties peerProperties) {
        if (!mIsShuttingDown) {
            if (mSettings.getAutoConnect() && !mModel.hasConnectionToPeer(peerProperties, false)) {
                if (mSettings.getAutoConnectEvenWhenIncomingConnectionEstablished()
                        || !mModel.hasConnectionToPeer(peerProperties, true)) {
                    // Do auto-connect
                    Log.i(TAG, "autoConnectIfEnabled: Auto-connecting to peer " + peerProperties.toString());
                    connect(peerProperties);
                    return true;
                }
            }
        }
        return false;
    }

    protected synchronized void restartNotifyStateChangedTimer() {
        if (mNotifyStateChangedTimer != null) {
            mNotifyStateChangedTimer.cancel();
            mNotifyStateChangedTimer = null;
        }

        mNotifyStateChangedTimer = new CountDownTimer(NOTIFY_STATE_CHANGED_DELAY_IN_MILLISECONDS,
                NOTIFY_STATE_CHANGED_DELAY_IN_MILLISECONDS) {
            @Override
            public void onTick(long l) {
                // Not used
            }

            @Override
            public void onFinish() {
                this.cancel();
                mNotifyStateChangedTimer = null;

                StringBuilder stringBuilder = new StringBuilder();
                stringBuilder.append("Connectivity: ");
                stringBuilder.append((mConnectionManager != null) ? mConnectionManager.getState() : "not running");
                stringBuilder.append(", discovery: ");
                stringBuilder.append((mDiscoveryManager != null) ? mDiscoveryManager.getState() : "not running");
                stringBuilder.append(", ");
                stringBuilder.append(
                        (mDiscoveryManager != null && mDiscoveryManager.isDiscovering()) ? "discovering/scanning"
                                : "not discovering/scanning");
                stringBuilder.append(", ");
                stringBuilder
                        .append((mDiscoveryManager != null && mDiscoveryManager.isAdvertising()) ? "advertising"
                                : "not advertising");
                String message = stringBuilder.toString();

                LogFragment.logMessage(message);
                MainActivity.showToast(message);
                BridgeSpot.statusConnectionEngine = ":" + message;
            }
        };

        mNotifyStateChangedTimer.start();
    }

    /**
     * Prompts the user to grant the given permission.
     *
     * @param permission The permission, which needs to be granted.
     */
    private void requestPermission(final String permission) {
        Log.i(TAG, "requestPermission: " + permission);

        // Should we show an explanation?
        if (ActivityCompat.shouldShowRequestPermissionRationale(mActivity, permission)) {
            // The app has requested this permission previously and the user denied the request.
            if (mAlertDialog == null) {
                AlertDialog.Builder alertDialogBuilder = new AlertDialog.Builder(mActivity);
                alertDialogBuilder
                        .setMessage("Location permission is required to scan for nearby peers using Bluetooth LE.");
                alertDialogBuilder.setCancelable(true);

                alertDialogBuilder.setPositiveButton("OK", new DialogInterface.OnClickListener() {
                    public void onClick(DialogInterface dialog, int id) {
                        dialog.cancel();
                        ActivityCompat.requestPermissions(mActivity, new String[] { permission },
                                PERMISSION_REQUEST_ACCESS_COARSE_LOCATION);
                    }
                });

                mAlertDialog = alertDialogBuilder.create();
            }

            if (!mAlertDialog.isShowing()) {
                mAlertDialog.show();
            }
        } else {
            // No explanation needed, we can request the permission.
            ActivityCompat.requestPermissions(mActivity, new String[] { permission },
                    PERMISSION_REQUEST_ACCESS_COARSE_LOCATION);
        }
    }
}