Android Open Source - Glass-IntentTunnel Bluetooth Service






From Project

Back to project page Glass-IntentTunnel.

License

The source code is released under:

MIT License

If you think the Android project Glass-IntentTunnel listed in this page is inappropriate, such as containing malicious code/tools or violating the copyright, please email info at java2s dot com, thanks.

Java Source Code

package com.masterbaron.intenttunnel.router;
//  www . j  av  a2 s .c o  m
import android.bluetooth.BluetoothAdapter;
import android.content.Intent;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.util.Base64;
import android.util.Log;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.net.URISyntaxException;
import java.util.ArrayList;
import java.util.List;
import java.util.Queue;
import java.util.Set;
import java.util.UUID;
import java.util.concurrent.TimeUnit;

import ktlab.lib.connection.ConnectionCallback;
import ktlab.lib.connection.ConnectionCommand;
import ktlab.lib.connection.bluetooth.BluetoothConnection;
import ktlab.lib.connection.bluetooth.ServerBluetoothConnection;

/**
 * Created by Van Etten on 12/6/13.
 */
public abstract class BluetoothService implements ConnectionCallback, Handler.Callback {
    private static final String UUID_BASE="-756C-11E3-981F-0800200C9A66";

    private static final String ENCODER_KEY_PREFIX = "IntentTunnel[byte]";
    private static final String ENCODER_KEY_LIST_STRING = "IntentTunnel.StringList";
    private static final String ENCODER_KEY_LIST_INTEGER = "IntentTunnel.IntegerList";

    protected static byte BLUETOOTH_COMMAND_BROADCAST_INTENT = 100;
    protected static byte BLUETOOTH_COMMAND_STARTSERVICE_INTENT = 101;
    protected static byte BLUETOOTH_COMMAND_STARTACTIVITY_INTENT = 102;

    private static final int MESSAGE_CHECK_TIMEOUT = 2300;
    private static final int MESSAGE_BT_FAIL = 2301;
    private static long CONNECTION_CHECK_INTERVAL = TimeUnit.SECONDS.toMillis(5);
    private static long CONNECTION_TIMEOUT = TimeUnit.SECONDS.toMillis(25);
    private static long CONNECTION_SERVER_TIMEOUT = TimeUnit.SECONDS.toMillis(15);

    protected Handler mHandler;
    protected BluetoothConnection mBTConnection;
    protected RouterService mRouterService;

    private String mStatus = "Stopped";
    private boolean isEnabled = true;
    private boolean isRunning = false;
    private boolean isConnected = false;
    private long lastActivity = 0;
    private int mMessageId = 0;
    private Packet sendingPacket;

    abstract protected BluetoothConnection createNewBTConnection();

    protected String getTag() {
        // logs to belong to whoever implements this abstract class.
        return ((Object) this).getClass().getSimpleName();
    }

    public boolean isRunning() {
        return isRunning;
    }

    public String getStatus() {
        return mStatus;
    }

    public boolean isConnected() {
        return isConnected;
    }

    public long getLastActivity() {
        return lastActivity;
    }

    public boolean isEnabled() {
        return isEnabled;
    }

    public boolean isSending() {
        return sendingPacket != null;
    }

    public BluetoothService(RouterService routerService) {
        Log.d(getTag(), "created()");
        this.mRouterService = routerService;

        mStatus = "Ready";

        // setup handler and messenger
        mHandler = new Handler(this);
    }

    public void stop() {
        Log.d(getTag(), "stopped()");
        mStatus = "Stopping";
        isEnabled = false;
        stopConnection();

        mStatus = "Stopped";
    }

    protected void stopConnection() {
        mStatus = "Disconnecting";
        if (mBTConnection != null) {
            mBTConnection.stopConnection();
            mBTConnection = null;
        }
        mHandler.removeCallbacksAndMessages(null);
        mMessageId = 0;
        isRunning = false;
        isConnected = false;
        mStatus = "Disconnected";
    }

    protected boolean startConnection() {
        mStatus = "Connecting";
        mMessageId = 0;
        isRunning = true;
        if (mRouterService.isBluetoothEnabled()) {
            Log.d(getTag(), "createNewBTConnection");
            mBTConnection = createNewBTConnection();
        }
        if (mBTConnection != null) {
            // if this is a server, then we will be waiting for a connection
            if (isBTServer()) {
                mStatus = "Waiting for connection";
            }

            mBTConnection.startConnection();
        } else {
            mStatus = "BT device failure";
            mHandler.sendEmptyMessage(MESSAGE_BT_FAIL);
        }
        return true;
    }

    @Override
    public void onConnectComplete() {
        Log.d(getTag(), "onConnectComplete()");
        mStatus = "Connected";
        isConnected = true;

        // once connected reset failure counter
        trackBluetoothActivity();

        mRouterService.onConnectComplete(this);

        // start the process of checking for inactivity
        mHandler.sendEmptyMessageDelayed(MESSAGE_CHECK_TIMEOUT, CONNECTION_CHECK_INTERVAL);
    }

    /**
     * called when we detect bluetooth activity.
     * used to determine idle timeout
     */
    private void trackBluetoothActivity() {
        lastActivity = System.currentTimeMillis();
    }

    @Override
    public void onConnectionFailed() {
        Log.d(getTag(), "onConnectionFailed()");
        mStatus = "Connection Failed";

        stopConnection();

        // when there is a failure, we need to tell the router it failed
        mRouterService.onIntentSendFail(this, sendingPacket);
        sendingPacket = null;
    }

    @Override
    public void onConnectionLost() {
        Log.d(getTag(), "onConnectionLost()");
        mStatus = "Connection Lost";

        stopConnection();

        // when there is a failure, we need to tell the router it failed
        mRouterService.onIntentSendFail(this, sendingPacket);
        sendingPacket = null;
    }

    @Override
    public void onDataSendComplete(int id) {
        Log.d(getTag(), "onDataSendComplete(" + id + ")");
        mStatus = "Ready (Sent Data)";
        trackBluetoothActivity();

        mRouterService.onIntentSendComplete(this, sendingPacket);
        sendingPacket = null;
    }

    @Override
    public void onCommandReceived(ConnectionCommand command) {
        Log.d(getTag(), "onCommandReceived(" + command.type + ")");

        if (command.type == BLUETOOTH_COMMAND_BROADCAST_INTENT) {
            broadcast(command.option);
        } else if (command.type == BLUETOOTH_COMMAND_STARTSERVICE_INTENT) {
            startService(command.option);
        } else if (command.type == BLUETOOTH_COMMAND_STARTACTIVITY_INTENT) {
            startActivity(command.option);
        }

        mStatus = "Ready (Received Data)";
        trackBluetoothActivity();
    }

    protected boolean isBTServer() {
        return this instanceof ServerService;
    }

    /**
     * Handle all internal and external messages
     */
    public boolean handleMessage(Message msg) {
        Log.d(getTag(), "act on what=" + msg.what);
        if (msg.what == RouterService.ROUTER_MESSAGE_BROADCAST_INTENT ) {
            try {
                mMessageId++;
                Intent intent = (Intent) msg.obj;
                String sendUri = encodeIntent(intent);
                mBTConnection.sendData(BLUETOOTH_COMMAND_BROADCAST_INTENT, sendUri.getBytes(), mMessageId);
            } catch (Exception e) {
                Log.e(getTag(), "failed to process BLUETOOTH_COMMAND_BROADCAST_INTENT", e);
            }
            return true;
        } else if (msg.what == RouterService.ROUTER_MESSAGE_STARTSERVICE_INTENT) {
            try {
                mMessageId++;
                Intent intent = (Intent) msg.obj;
                String sendUri = encodeIntent(intent);
                mBTConnection.sendData(BLUETOOTH_COMMAND_STARTSERVICE_INTENT, sendUri.getBytes(), mMessageId);
            } catch (Exception e) {
                Log.e(getTag(), "failed to process BLUETOOTH_COMMAND_STARTSERVICE_INTENT", e);
            }
            return true;
        } else if (msg.what == RouterService.ROUTER_MESSAGE_STARTACTIVITY_INTENT) {
            try {
                mMessageId++;
                Intent intent = (Intent) msg.obj;
                String sendUri = encodeIntent(intent);
                mBTConnection.sendData(BLUETOOTH_COMMAND_STARTACTIVITY_INTENT, sendUri.getBytes(), mMessageId);
            } catch (Exception e) {
                Log.e(getTag(), "failed to process BLUETOOTH_COMMAND_STARTACTIVITY_INTENT", e);
            }
            return true;
        } else if (msg.what == MESSAGE_CHECK_TIMEOUT) { // inactivity checking
            if (mBTConnection != null && isConnected()) {
                if (!mBTConnection.isSending() && !mBTConnection.hasPending()) {
                    long timeout = CONNECTION_TIMEOUT;
                    if (isBTServer()) {
                        timeout = CONNECTION_SERVER_TIMEOUT;
                    }

                    if (getLastActivity() + timeout < System.currentTimeMillis()) {
                        Log.d(getTag(), "MESSAGE_CHECK_TIMEOUT.  stopping connection");
                        onConnectionLost();
                    }
                }
                if (isConnected()) {
                    mHandler.sendEmptyMessageDelayed(MESSAGE_CHECK_TIMEOUT, CONNECTION_CHECK_INTERVAL);
                }
            }
            return true;
        } else if (msg.what == MESSAGE_BT_FAIL) { // inactivity checking
            onConnectionFailed();
        }

        return false;
    }

    public void sendIntent(Packet msg) {
        sendingPacket = msg;
        mHandler.obtainMessage(msg.getType(), msg.getIntent()).sendToTarget();
    }

    protected void broadcast(byte[] option) {
        String uri = new String(option);
        try {
            Intent intent = decodeIntent(uri);
            Log.d(getTag(), "Broadcasting Intent: " + intent);
            mRouterService.sendBroadcast(intent);
        } catch (Exception e) {
            Log.e(getTag(), "Invalid URI: " + uri, e);
        }
    }

    protected void startService(byte[] option) {
        String uri = new String(option);
        try {
            Intent intent = decodeIntent(uri);
            Log.d(getTag(), "startService Intent: " + intent);
            mRouterService.startService(intent);
        } catch (Exception e) {
            Log.e(getTag(), "Invalid URI: " + uri, e);
        }
    }

    protected void startActivity(byte[] option) {
        String uri = new String(option);
        try {
            Intent intent = decodeIntent(uri);
            Log.d(getTag(), "startService Intent: " + intent);
            mRouterService.startActivity(intent);
        } catch (Exception e) {
            Log.e(getTag(), "Invalid URI: " + uri, e);
        }
    }

    protected String encodeIntent(Intent intent) {
        Bundle extras = intent.getExtras();
        if ( extras != null ) {
            Set<String> keys = extras.keySet();
            for (String key : keys) {
                Object value = extras.get(key);
                if (value instanceof byte[]) {
                    String encoded = Base64.encodeToString((byte[]) value, 0);
                    intent.putExtra(ENCODER_KEY_PREFIX + key, encoded);
                    intent.removeExtra(key);
                } else if (value instanceof List) {
                    boolean stringList = (extras.getStringArrayList(key) != null);
                    boolean intList = (extras.getIntegerArrayList(key) != null);
                    try {
                        ByteArrayOutputStream baos = new ByteArrayOutputStream();
                        ObjectOutputStream out = new ObjectOutputStream(baos);
                        out.writeObject((List) value);
                        out.close();
                        baos.close();

                        String encoded = Base64.encodeToString(baos.toByteArray(), 0);
                        intent.removeExtra(key);

                        if (stringList) {
                            intent.putExtra(ENCODER_KEY_LIST_STRING + key, encoded);
                        }
                        if (intList) {
                            intent.putExtra(ENCODER_KEY_LIST_INTEGER + key, encoded);
                        }
                    } catch (IOException e) {
                        Log.e(getTag(), "Invalid URI", e);
                    }
                }
            }
        }

        return intent.toUri(Intent.URI_INTENT_SCHEME);
    }

    protected Intent decodeIntent(String uri) throws URISyntaxException {
        Intent intent = Intent.parseUri(uri, Intent.URI_INTENT_SCHEME);

        Bundle extras = intent.getExtras();
        if ( extras != null ) {
            Set<String> keys = extras.keySet();
            for (String key : keys) {
                if (key.startsWith(ENCODER_KEY_PREFIX)) {
                    String newKey = key.substring(ENCODER_KEY_PREFIX.length());
                    intent.putExtra(newKey, Base64.decode(extras.getString(key), 0));
                    intent.removeExtra(key);
                } else if (key.startsWith(ENCODER_KEY_LIST_STRING) || key.startsWith(ENCODER_KEY_LIST_INTEGER)) {
                    try {
                        String newKey;
                        if (key.startsWith(ENCODER_KEY_LIST_STRING)) {
                            newKey = key.substring(ENCODER_KEY_LIST_STRING.length());
                        } else if (key.startsWith(ENCODER_KEY_LIST_INTEGER)) {
                            newKey = key.substring(ENCODER_KEY_LIST_INTEGER.length());
                        } else {
                            throw new IOException("Invalid URI");
                        }

                        ByteArrayInputStream bais = new ByteArrayInputStream(Base64.decode(extras.getString(key), 0));
                        ObjectInputStream in = new ObjectInputStream(bais);
                        ArrayList<?> list = (ArrayList<?>) in.readObject();
                        intent.removeExtra(key);
                        if (key.startsWith(ENCODER_KEY_LIST_STRING)) {
                            intent.putExtra(newKey, (ArrayList<String>) list);
                        } else if (key.startsWith(ENCODER_KEY_LIST_INTEGER)) {
                            intent.putExtra(newKey, (ArrayList<Integer>) list);
                        }
                    } catch (IOException e) {
                        Log.e(getTag(), "Invalid URI", e);
                    } catch (ClassNotFoundException e) {
                        Log.e(getTag(), "Invalid URI", e);
                    }
                } else if (key.startsWith(ENCODER_KEY_LIST_INTEGER)) {
                    String newKey = key.substring(ENCODER_KEY_LIST_INTEGER.length());
                    byte[] list = Base64.decode(extras.getString(key), 0);
                    intent.removeExtra(key);
                }
            }
        }
        return intent;
    }

    protected UUID getUUIDFromAddress(String address) {
        long num = Math.abs(address.hashCode() % 99999999);
        String prefix = String.format("%08d", num);
        return UUID.fromString(prefix + UUID_BASE);
    }
}




Java Source Code List

com.masterbaron.intenttunnel.IntentTunnelActivity.java
com.masterbaron.intenttunnel.android.AndroidConfigureActivity.java
com.masterbaron.intenttunnel.android.DeviceSelectActivity.java
com.masterbaron.intenttunnel.glass.DeviceSelectActivity.java
com.masterbaron.intenttunnel.glass.GlassConfigureActivity.java
com.masterbaron.intenttunnel.router.BluetoothService.java
com.masterbaron.intenttunnel.router.ClientService.java
com.masterbaron.intenttunnel.router.Packet.java
com.masterbaron.intenttunnel.router.RouterService.java
com.masterbaron.intenttunnel.router.ServerService.java
com.masterbaron.intenttunnel.router.ServiceReceiver.java
com.masterbaron.musicpusher.MusicActivity.java
ktlab.lib.connection.CommandReceiveThread.java
ktlab.lib.connection.CommandSendThread.java
ktlab.lib.connection.ConnectionCallback.java
ktlab.lib.connection.ConnectionCommand.java
ktlab.lib.connection.ConnectionThread.java
ktlab.lib.connection.Connection.java
ktlab.lib.connection.bluetooth.BluetoothConnectionThread.java
ktlab.lib.connection.bluetooth.BluetoothConnection.java
ktlab.lib.connection.bluetooth.ClientBluetoothConnectionThread.java
ktlab.lib.connection.bluetooth.ClientBluetoothConnection.java
ktlab.lib.connection.bluetooth.ServerBluetoothConnectionThread.java
ktlab.lib.connection.bluetooth.ServerBluetoothConnection.java