导语:你可以利用org.quietmodem.Quiet在你的安卓设备上使用扬声器传输数据,可以作为框架层或UDP/TCP栈来进行操作。

你可以利用org.quietmodem.Quiet在你的安卓设备上使用扬声器传输数据,可以作为框架层或UDP/TCP栈来进行操作。
它包含了 libquiet和quiet-lwip及其附属类的预编译库文件,并且加入了高度模拟java.net.* package的接口。它遵守3-clause BSD协议,附属包也都具有混合了MIT与BSD的许可。
Quiet包可以被armeabi-v7a, arm64-v8a, x86和x86_64系统支持,它在32位模式时需要 Android API 版本为14,而在64位模式时需要 Android API 版本为16,任意模式下只需要RECORD_AUDIO许可。
强烈推荐使用Genymotion模拟器来做测试,因为它提供了麦克风使用权,这在默认的Android Studio中是没有的,Quiet使用麦克风时Android Studio会抛出异常。
Quiet的优点
如果你是一位很有资历的研究人员,你应该还记得从前我们用拨号的猫上网,从某种角度来说,Quiet包使用的方法与其相似。当然不得不说这确实是一种复古的方法,但也有其优点:
可跨平台:任何具有扬声器、麦克风以及足够的计算能力的设备都可以通过它进行传输。
不使用配对模式:它不像蓝牙那样麻烦,可以不通过设备配对直接使用音频,这样减少了冲突并提高了用户体验。
可嵌入信息:与QR码类似,简短的数据可以被编码到流音频或音频录制中,传输后再进行解码。
这个问题取决于你选择哪种操作模式,Quiet提供了可听见的正常模式,与超声波模式。可听见模式下听起来像一阵风吹过,超声波模式的频率超过17千赫(kHz),成年人基本无法听到。如果环境不太吵,两种模式都可以在相对低的音量下使用。
Quiet在可听见模式下的速度大约是7kbps。如果两个设备通过电缆连接(使用3.5mm音频插头),Quiet可以在电缆模式下工作,速度可以达到64kbps。
台式机/笔记本:libquiet 和 quiet-lwip Javascript: quiet-js (UDP/TCP版本即将发布) iOS:即将发布
Quiet可以作为框架层使用,也可以在UDP/TCP模式下使用。对于后者,它提供了lwIP TCP栈,该栈相对于安卓提供的栈是可以完全独立执行的。
付诸于实践
必须将Android NDK安装在local.properties中的ndk.dir下,这是为了在Quiet中建立JNI包裹文件。
假设我们从MainActivity.java开始工作:
importjava.io.IOException;
importorg.quietmodem.Quiet.*;
FrameReceiverConfigreceiverConfig = null;
FrameTransmitterConfigtransmitterConfig = null;
try {
transmitterConfig = newFrameTransmitterConfig(
this,
"audible-7k-channel-0");
receiverConfig = new FrameReceiverConfig(
this,
"audible-7k-channel-0");
} catch(IOException e) {
// could not build configs
}
FrameReceiverreceiver = null;
FrameTransmittertransmitter = null;
try {
receiver = newFrameReceiver(receiverConfig);
transmitter = newFrameTransmitter(transmitterConfig);
} catch(ModemException e) {
// could not set up receiver/transmitter
}
这建立了传输器和接收器。这里我们选择可听见的模式。我们可以这样
// set receiver toblock for at most 1 second
// by defaultreceivers are nonblocking
receiver.setBlocking(1,0);
byte[] buf = newbyte[1024];
long recvLen = 0;
try {
recvLen = receiver.receive(buf);
} catch(IOException e) {
// read timed out
}
也可以这样
String payload ="Hello, World!";
try {
transmitter.send(payload.getBytes());
} catch(IOException e) {
// our message might be too long or thetransmit queue full
}
这就足够用来发送框架了。当我们想发送很短的数据,这些数据不需要发送器或接收器,可以简单的装在一个frame中时,我们就会使用框架模式,也就是说,框架是广播媒体。
如果我们想让两个设备进行互动,或者我们想让数据自动分组转发,我们就要用到UDP/TCP模式。
首先建立新的NetworkInterface
importjava.io.IOException;
importjava.net.SocketException;
importorg.quietmodem.Quiet.*;
FrameReceiverConfigreceiverConfig = null;
FrameTransmitterConfigtransmitterConfig = null;
try {
transmitterConfig = newFrameTransmitterConfig(
this,
"audible-7k-channel-0");
receiverConfig = new FrameReceiverConfig(
this,
"audible-7k-channel-0");
} catch(IOException e) {
// could not build configs
}
NetworkInterfaceConfigconf = new NetworkInterfaceConfig(
receiverConfig,
transmitterConfig);
NetworkInterfaceintf = null;
try {
intf = new NetworkInterface(conf);
} catch(ModemException e) {
// network interface failure
}
例子中省略了NetworkInterfaceConfig()中建立Auto IP的IP地址和网络掩码,它会自动给我们的接口赋予一个地址,一旦我们将接口实例化,探测并决定该地址需要数秒的时间。
如果我们使用点对点模式,就需要寻找附近的客户端,我们可以通过广播UDP包来完成。
我们可以像下面这样来实现:
// org.quietmodem.Quiet.DatagramSocket
DatagramSocket s =null;
try {
// bind to wildcard:3333 -- note that thisis
// usingorg.quietmodem.Quiet.InetSocketAddress
// not java.net.InetSocketAddress
sSend = new DatagramSocket(newInetSocketAddress("0.0.0.0", 3333));
// listen on 3334
sRecv = new DatagramSocket(newInetSocketAddress("0.0.0.0", 3334));
// don't block for more than 10 seconds
sRecv.setSoTimeout(10000);
// get broadcast permission
sSend.setBroadcast(true);
} catch(SocketException e) {
// exception creating DatagramSocket
}
byte[] send ="MARCO".getBytes();
byte[] recv = newbyte[1024];
InetSocketAddresspeer = null;
while (true) {
// get ready to do a broadcast to port 3334
// again, this is org.quietmodem.Quiet.DatagramPacket
DatagramPacket p = new DatagramPacket(send,send.length,
newInetSocketAddress("169.254.255.255", 3334));
try {
sSend.send(p);
} catch (IOException e) {
// exception sending on DatagramSocket
}
DatagramPacket pRecv = newDatagramPacket(recv, recv.length);
boolean received = false;
try {
sRecv.receive(pRecv);
received = true;
peer = pRecv.getSocketAddress();
// respond so that the other peer knowswe're here
p.setData("POLO".getBytes());
p.setSocketAddress(peer);
sSend.send(p);
} catch (IOException e) {
// exception receiving onDatagramSocket
}
if (received) {
break;
}
// our 10-second read timeout acts as asleep
// continue broadcasting until we get abite
}
// now use thepeer's address somehow....
// continuesending UDP or establish a TCP connection
// on another port
// when finished,close sSend, sRecv and intf