/* * Copyright 2014 Bevbot LLC <info@bevbot.com> * * This file is part of the Kegtab package from the Kegbot project. For * more information on Kegtab or Kegbot, see <http://kegbot.org/>. * * Kegtab 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, version 2. * * Kegtab 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 Kegtab. If not, see <http://www.gnu.org/licenses/>. */ package org.kegbot.core; import android.bluetooth.BluetoothAdapter; import android.bluetooth.BluetoothServerSocket; import android.bluetooth.BluetoothSocket; import android.content.Context; import android.os.Binder; import android.util.Log; import com.google.common.base.Strings; import com.hoho.android.usbserial.util.HexDump; import com.squareup.otto.Bus; import org.codehaus.jackson.JsonNode; import org.codehaus.jackson.map.ObjectMapper; import org.kegbot.app.AuthenticatingActivity; import java.io.IOException; import java.io.InputStream; import java.util.UUID; /** * Listens for bluetooth connections. * * @author mike wakerly (mike@wakerly.com) */ public class BluetoothManager extends BackgroundManager { private static final String TAG = BluetoothManager.class.getSimpleName(); private static final String KEGTAB_SERVICE_NAME = "kegtab"; public static final UUID KEGTAB_BT_UUID = UUID.fromString("50c93109-154d-49c0-8565-4d63abf25615"); private final Context mContext; private boolean mQuit; public class LocalBinder extends Binder { BluetoothManager getService() { return BluetoothManager.this; } } public BluetoothManager(Bus bus, Context context) { super(bus); mContext = context; } @Override public synchronized void start() { mQuit = false; super.start(); } @Override public synchronized void stop() { mQuit = true; super.stop(); } @Override protected void runInBackground() { Log.d(TAG, "Running in background."); while (true) { synchronized (this) { if (mQuit) { Log.i(TAG, "Exiting."); break; } } final BluetoothAdapter adapter = getUsableAdapter(); if (adapter == null) { Log.w(TAG, "No usable adapter, exiting."); break; } try { handleOneConnection(adapter); } catch (IOException e) { Log.w(TAG, "Connection failed: " + e.toString(), e); break; } catch (SecurityException e) { Log.w(TAG, "Connection failed: " + e.toString(), e); break; } } } private BluetoothAdapter getUsableAdapter() { final BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter(); if (adapter == null) { Log.w(TAG, "Bluetooth not supported."); return null; } else if (!adapter.isEnabled()) { Log.w(TAG, "Bluetooth is not enabled."); return null; } return adapter; } private void handleOneConnection(BluetoothAdapter adapter) throws IOException { final BluetoothServerSocket serverSocket = adapter.listenUsingRfcommWithServiceRecord( KEGTAB_SERVICE_NAME, KEGTAB_BT_UUID); boolean serverSocketClosed = false; try { BluetoothSocket socket = null; while (socket == null) { socket = serverSocket.accept(); } Log.d(TAG, "Client accepted, closing server socket."); serverSocket.close(); serverSocketClosed = true; try { handleConnection(socket); } finally { Log.d(TAG, "Closing client socket."); socket.close(); } } finally { if (!serverSocketClosed) { Log.d(TAG, "Closing server socket due to abnormal exit."); serverSocket.close(); } } } private void handleConnection(BluetoothSocket socket) throws IOException { final InputStream inputStream = socket.getInputStream(); final byte buffer[] = new byte[4096]; int nBytes; while (true) { nBytes = inputStream.read(buffer); Log.d(TAG, "Read " + nBytes + " bytes."); Log.d(TAG, HexDump.dumpHexString(buffer, 0, nBytes)); JsonNode message = new ObjectMapper().readValue(buffer, 0, nBytes, JsonNode.class); Log.d(TAG, "Parsed message: " + message); handleMessage(message); } } private void handleMessage(JsonNode root) { final JsonNode commandNode = root.get("command"); if (commandNode == null || !commandNode.isValueNode()) { Log.d(TAG, "Kegnet BT message missing command."); return; } final JsonNode argsNode = root.get("args"); if (argsNode == null) { Log.d(TAG, "Kegnet BT message missing command."); return; } final String commandName = commandNode.getValueAsText(); if (Strings.isNullOrEmpty(commandName)) { Log.d(TAG, "Kegnet BT message empty command."); return; } Log.d(TAG, "Kegnet BT message command=" + commandName); if (commandName.equals("tokenAuth")) { final JsonNode authDevice = argsNode.get("authDevice"); final JsonNode tokenValue = argsNode.get("tokenValue"); if (authDevice == null || !authDevice.isValueNode() || tokenValue == null || !tokenValue.isValueNode()) { Log.d(TAG, "Kegnet BT malformed auth command."); return; } AuthenticatingActivity.startAndAuthenticate( mContext, authDevice.getTextValue(), tokenValue.getTextValue()); } } }