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

77.png

你可以利用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
源链接

Hacking more

...