Merge branch 'bluetooth_tnc' into master

smart_log
Georg Lukas 2011-09-05 15:30:24 +02:00
commit 7021857c5d
8 zmienionych plików z 289 dodań i 1 usunięć

Wyświetl plik

@ -9,6 +9,7 @@
android:targetSdkVersion="8" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/>
<uses-permission android:name="android.permission.BLUETOOTH" />
<uses-permission android:name="android.permission.INTERNET"/>
<uses-permission android:name="android.permission.VIBRATE"/>

Plik binarny nie jest wyświetlany.

Wyświetl plik

@ -5,12 +5,14 @@
<item>@string/p_conn_udp</item>
<item>@string/p_conn_http</item>
<item>@string/p_conn_afsk</item>
<item>@string/p_conn_bt</item>
</string-array>
<string-array name="p_conntype_ev">
<item>tcp</item>
<item>udp</item>
<item>http</item>
<item>afsk</item>
<item>bluetooth</item>
</string-array>
<string-array name="p_gps_e">

Wyświetl plik

@ -157,6 +157,7 @@
<string name="p_conn_udp">UDP (send only)</string>
<string name="p_conn_http">HTTP POST (send only)</string>
<string name="p_conn_afsk">AFSK via Speaker</string>
<string name="p_conn_bt">Bluetooth TNC</string>
<string name="p_host">Server</string>
<string name="p_host_summary">APRS-IS server (port 8080) to send beacons</string>

Wyświetl plik

@ -0,0 +1,38 @@
<?xml version="1.0" encoding="utf-8"?>
<PreferenceScreen
xmlns:android="http://schemas.android.com/apk/res/android">
<PreferenceCategory
android:title="Bluetooth">
<CheckBoxPreference
android:key="bt.client"
android:title="Client Mode"
android:defaultValue="true"
android:summary="Keep this on!" />
<de.duenndns.BluetoothDevicePreference
android:key="bt.mac"
android:dependency="bt.client"
android:title="TNC Bluetooth Device"
android:summary="You need to pair it from the system preferences"
android:dialogTitle="choose device" />
<EditTextPreference
android:key="bt.channel"
android:dependency="bt.client"
android:inputType="number"
android:title="Channel"
android:summary="Usually this is '1'"
android:dialogTitle="enter your channel" />
<EditTextPreference
android:key="digi_path"
android:hint="hop1,hop2,.."
android:defaultValue="WIDE1-1"
android:title="@string/p_aprs_path"
android:summary="@string/p_aprs_path_summary"
android:dialogTitle="@string/p_aprs_path_entry" />
</PreferenceCategory>
</PreferenceScreen>

Wyświetl plik

@ -0,0 +1,33 @@
package de.duenndns;
import android.bluetooth.*;
import android.content.Context;
import android.preference.ListPreference;
import android.util.AttributeSet;
import java.util.Set;
public class BluetoothDevicePreference extends ListPreference {
public BluetoothDevicePreference(Context context, AttributeSet attrs) {
super(context, attrs);
BluetoothAdapter bta = BluetoothAdapter.getDefaultAdapter();
Set<BluetoothDevice> pairedDevices = bta.getBondedDevices();
CharSequence[] entries = new CharSequence[pairedDevices.size()];
CharSequence[] entryValues = new CharSequence[pairedDevices.size()];
int i = 0;
for (BluetoothDevice dev : pairedDevices) {
entries[i] = dev.getName();
entryValues[i] = dev.getAddress();
i++;
}
setEntries(entries);
setEntryValues(entryValues);
}
public BluetoothDevicePreference(Context context) {
this(context, null);
}
}

Wyświetl plik

@ -22,7 +22,11 @@ object Backend {
"tcp" -> new BackendInfo(
(s, p) => new TcpUploader(s, p),
R.xml.pref_tcp,
PASSCODE_OPTIONAL)
PASSCODE_OPTIONAL),
"bluetooth" -> new BackendInfo(
(s, p) => new BluetoothTnc(s, p),
R.xml.pref_bluetooth,
PASSCODE_NONE)
)
def defaultBackendInfo(prefs : PrefsWrapper) : BackendInfo = {

Wyświetl plik

@ -0,0 +1,209 @@
package org.aprsdroid.app
import _root_.android.bluetooth._
import _root_.android.app.Service
import _root_.android.content.Intent
import _root_.android.location.Location
import _root_.android.util.Log
import _root_.java.io.{InputStream, OutputStream}
import _root_.java.net.{InetAddress, Socket}
import _root_.java.util.UUID
import _root_.net.ab0oo.aprs.parser._
class BluetoothTnc(service : AprsService, prefs : PrefsWrapper) extends AprsIsUploader(prefs) {
val TAG = "APRSdroid.Bluetooth"
val SPP = UUID.fromString("00001101-0000-1000-8000-00805F9B34FB")
val bt_client = prefs.getBoolean("bt.client", true)
val tncmac = prefs.getString("bt.mac", null)
val tncchannel = prefs.getStringInt("bt.channel", -1)
var digipath = prefs.getString("digi_path", "WIDE1-1")
var conn : BtSocketThread = null
createConnection()
def start() {
}
def createConnection() {
Log.d(TAG, "BluetoothTnc.createConnection: " + tncmac)
val adapter = BluetoothAdapter.getDefaultAdapter()
if (adapter == null) {
service.postAbort("Bluetooth not supported!")
return
}
if (!adapter.isEnabled()) {
service.postAbort("Bluetooth not enabled!")
return
}
val tnc = if (bt_client) adapter.getRemoteDevice(tncmac) else null
conn = new BtSocketThread(adapter, tnc)
conn.start()
}
def update(packet : APRSPacket) : String = {
packet.setDigipeaters(Digipeater.parseList(digipath, true))
Log.d(TAG, "BluetoothTnc.update: " + packet)
conn.update(packet)
}
def stop() {
if (conn == null)
return
conn.shutdown()
conn.interrupt()
conn.join(50)
}
class BtSocketThread(ba : BluetoothAdapter, tnc : BluetoothDevice)
extends Thread("APRSdroid Bluetooth connection") {
val TAG = "BtSocketThread"
var running = false
var socket : BluetoothSocket = null
var reader : KissReader = null
var writer : KissWriter = null
def init_socket() {
Log.d(TAG, "init_socket()")
if (socket != null) {
shutdown()
}
if (tnc == null) {
// we are a host
Log.d(TAG, "awaiting client connection...")
socket = ba.listenUsingRfcommWithServiceRecord("SPP", SPP).accept(-1)
Log.d(TAG, "client connected.")
} else
if (tncchannel == -1) {
Log.d(TAG, "Connecting to SPP service...")
socket = tnc.createRfcommSocketToServiceRecord(SPP)
socket.connect()
} else {
Log.d(TAG, "Connecting to channel %d...".format(tncchannel))
val m = tnc.getClass().getMethod("createRfcommSocket", classOf[Int])
socket = m.invoke(tnc, tncchannel.asInstanceOf[AnyRef]).asInstanceOf[BluetoothSocket]
socket.connect()
}
this.synchronized {
reader = new KissReader(socket.getInputStream())
writer = new KissWriter(socket.getOutputStream())
running = true
}
Log.d(TAG, "init_socket() done")
}
override def run() {
Log.d(TAG, "BtSocketThread.run()")
try {
init_socket()
} catch {
case e : Exception => e.printStackTrace(); service.postAbort(e.toString())
}
while (running) {
try {
Log.d(TAG, "waiting for data...")
while (running) {
val line = reader.readPacket()
Log.d(TAG, "recv: " + line)
service.postSubmit(line)
}
} catch {
case e : Exception =>
e.printStackTrace()
Log.d(TAG, "reconnecting in 3s")
try {
Thread.sleep(3*1000)
init_socket()
} catch { case _ => }
}
}
Log.d(TAG, "BtSocketThread.terminate()")
}
def update(packet : APRSPacket) : String = {
if (socket != null) {
writer.writePacket(packet.toAX25Frame())
"Bluetooth OK"
} else "Bluetooth disconnected"
}
def catchLog(tag : String, fun : ()=>Unit) {
Log.d(TAG, "catchLog(" + tag + ")")
try {
fun()
} catch {
case e : Exception => e.printStackTrace(); Log.d(TAG, tag + " execption: " + e)
}
}
def shutdown() {
Log.d(TAG, "shutdown()")
this.synchronized {
running = false
catchLog("socket.close", socket.close)
}
}
}
object Kiss {
// escape sequences
val FEND = 0xC0
val FESC = 0xDB
val TFEND = 0xDC
val TFESC = 0xDD
// commands
val CMD_DATA = 0x00
}
class KissReader(is : InputStream) {
def readPacket() : String = {
import Kiss._
val buf = scala.collection.mutable.ListBuffer[Byte]()
do {
var ch = is.read()
Log.d(TAG, "KissReader.readPacket: %02X '%c'".format(ch, ch))
ch match {
case FEND =>
if (buf.length > 0) {
Log.d(TAG, "KissReader.readPacket: sending back %s".format(new String(buf.toArray)))
try {
return Parser.parseAX25(buf.toArray).toString().trim()
} catch {
case e => buf.clear()
}
}
case FESC => is.read() match {
case TFEND => buf.append(FEND.toByte)
case TFESC => buf.append(FESC.toByte)
case _ =>
}
case -1 => throw new java.io.IOException("KissReader out of data")
case 0 =>
// hack: ignore 0x00 byte at start of frame, this is the command
if (buf.length != 0)
buf.append(ch.toByte)
else
Log.d(TAG, "KissReader.readPacket: ignoring command byte")
case _ =>
buf.append(ch.toByte)
}
} while (true)
""
}
}
class KissWriter(os : OutputStream) {
def writePacket(p : Array[Byte]) {
Log.d(TAG, "KissWriter.writePacket: %s".format(p))
os.write(Kiss.FEND)
os.write(Kiss.CMD_DATA)
os.write(p)
os.write(Kiss.FEND)
os.flush()
}
}
}